From 2332d1ff225ce3652d466e17fd04a49742fb4c74 Mon Sep 17 00:00:00 2001 From: Pierre Sassoulas Date: Sat, 2 May 2020 08:16:16 +0200 Subject: [PATCH 1/4] Remove all tests concerning bad-continuation --- tests/functional/b/bad_continuation.py | 232 ------------------- tests/functional/b/bad_continuation.txt | 63 ----- tests/functional/b/bad_continuation_py36.py | 8 - tests/functional/b/bad_continuation_py36.rc | 2 - tests/functional/b/bad_continuation_tabs.py | 52 ----- tests/functional/b/bad_continuation_tabs.rc | 3 - tests/functional/b/bad_continuation_tabs.txt | 6 - tests/input/func_bad_cont_dictcomp_py27.py | 38 --- 8 files changed, 404 deletions(-) delete mode 100644 tests/functional/b/bad_continuation.py delete mode 100644 tests/functional/b/bad_continuation.txt delete mode 100644 tests/functional/b/bad_continuation_py36.py delete mode 100644 tests/functional/b/bad_continuation_py36.rc delete mode 100644 tests/functional/b/bad_continuation_tabs.py delete mode 100644 tests/functional/b/bad_continuation_tabs.rc delete mode 100644 tests/functional/b/bad_continuation_tabs.txt delete mode 100644 tests/input/func_bad_cont_dictcomp_py27.py diff --git a/tests/functional/b/bad_continuation.py b/tests/functional/b/bad_continuation.py deleted file mode 100644 index 8468f770b3..0000000000 --- a/tests/functional/b/bad_continuation.py +++ /dev/null @@ -1,232 +0,0 @@ -"""Regression test case for bad-continuation.""" -# pylint: disable=print-statement,implicit-str-concat,using-constant-test,missing-docstring,wrong-import-position -# Various alignment for brackets -from __future__ import print_function - -LIST0 = [ - 1, 2, 3 -] -LIST1 = [ - 1, 2, 3 - ] -LIST2 = [ - 1, 2, 3 - ] # [bad-continuation] - -# Alignment inside literals -W0 = [1, 2, 3, - 4, 5, 6, - 7, # [bad-continuation] - 8, 9, 10, - 11, 12, 13, - # and a comment - 14, 15, 16] - -W1 = { - 'a': 1, - 'b': 2, # [bad-continuation] - 'c': 3, - } - -W2 = { - 'a': 1, - 'b': 2, # [bad-continuation] - 'c': 3, - } - -W2 = ['some', 'contents' # with a continued comment that may be aligned - # under the previous comment (optionally) - 'and', - 'more', # but this - # [bad-continuation] is not accepted - 'contents', # [bad-continuation] nor this. - ] - -# Values in dictionaries should be indented 4 spaces further if they are on a -# different line than their key -W4 = { - 'key1': - 'value1', # Grandfather in the old style - 'key2': - 'value2', # [bad-continuation] - 'key3': - 'value3', # Comma here - } - -# And should follow the same rules as continuations within parens -W5 = { - 'key1': 'long value' - 'long continuation', - 'key2': 'breaking' - 'wrong', # [bad-continuation] - 'key3': 2*( - 2+2), - 'key4': ('parenthesis', - 'continuation') # No comma here - } - -# Allow values to line up with their keys when the key is next to the brace -W6 = {'key1': - 'value1', - 'key2': - 'value2', - } - -# Or allow them to be indented -W7 = {'key1': - 'value1', - 'key2': - 'value2' - } - -# Bug that caused a warning on the previous two cases permitted these odd -# incorrect indentations -W8 = {'key1': -'value1', # [bad-continuation] - } - -W9 = {'key1': - 'value1', # [bad-continuation] - } - -# Alignment of arguments in function definitions -def continue1(some_arg, - some_other_arg): - """A function with well-aligned arguments.""" - print(some_arg, some_other_arg) - - -def continue2( - some_arg, - some_other_arg): - """A function with well-aligned arguments.""" - print(some_arg, some_other_arg) - -def continue3( - some_arg, # [bad-continuation] - some_other_arg): # [bad-continuation] - """A function with misaligned arguments""" - print(some_arg, some_other_arg) - -def continue4( # pylint:disable=missing-docstring - arg1, - arg2): print(arg1, arg2) - - -def callee(*args): - """noop""" - print(args) - - -callee( - "a", - "b" - ) - -callee("a", - "b") # [bad-continuation] - -callee(5, {'a': 'b', - 'c': 'd'}) - -if ( - 1 - ): pass - -if ( - 1 -): pass -if ( - 1 - ): pass # [bad-continuation] - -if (1 and - 2): # [bad-continuation] - pass - -while (1 and - 2): - pass - -while (1 and - 2 and # [bad-continuation] - 3): - pass - -if ( - 2): pass # [bad-continuation] - -if (1 or - 2 or - 3): pass - -if (1 or - 2 or # [bad-continuation] - 3): print(1, 2) - -if (1 and - 2): pass # [bad-continuation] - -if ( - 2): pass - -if ( - 2): # [bad-continuation] - pass - -L1 = (lambda a, - b: a + b) - -if not (1 and - 2): - print(3) - -if not (1 and - 2): # [bad-continuation] - print(3) - -continue2("foo", - some_other_arg="this " - "is " - "fine") - -from contextlib import contextmanager -@contextmanager -def mycontext(*args): - yield args - -with mycontext( - "this is", - "great stuff", - "mane"): - pass - -# pylint: disable=using-constant-test -# More indentation included to distinguish this from the rest. -def long_function_name( - var_one, var_two, var_three, - var_four): - print(var_one, var_two, var_three, var_four) - - -def short_func_name(first, second, third): - # Add some extra indentation on the conditional continuation line. - if (first - and second == first == 'some_big_long_statement_that_should_not_trigger'): - third() - - -# Some normal multi-line statements with double-indented continuation lines. -LARGE_COLLECTION = [ - "spam", - "eggs", - "beans", - ] - -long_function_name( - "1", "2", "3", "4") - -CONCATENATED_TEXT = ( - "spam" - "eggs" - "beans") diff --git a/tests/functional/b/bad_continuation.txt b/tests/functional/b/bad_continuation.txt deleted file mode 100644 index 672f0afd43..0000000000 --- a/tests/functional/b/bad_continuation.txt +++ /dev/null @@ -1,63 +0,0 @@ -bad-continuation:14::"Wrong hanging indentation. - ] # [bad-continuation] -| ^|" -bad-continuation:19::"Wrong continued indentation (remove 3 spaces). - 7, # [bad-continuation] - | ^" -bad-continuation:27::"Wrong hanging indentation (add 1 space). - 'b': 2, # [bad-continuation] - ^|" -bad-continuation:33::"Wrong hanging indentation (add 1 space). - 'b': 2, # [bad-continuation] - ^|" -bad-continuation:41::"Wrong continued indentation. - # [bad-continuation] is not accepted - | | ^" -bad-continuation:42::"Wrong continued indentation (remove 20 spaces). - 'contents', # [bad-continuation] nor this. - | ^" -bad-continuation:51::"Wrong hanging indentation in dict value. - 'value2', # [bad-continuation] - | ^ |" -bad-continuation:61::"Wrong continued indentation (add 4 spaces). - 'wrong', # [bad-continuation] - ^ |" -bad-continuation:85::"Wrong hanging indentation in dict value. -'value1', # [bad-continuation] -^ | |" -bad-continuation:89::"Wrong hanging indentation in dict value. - 'value1', # [bad-continuation] - ^ | |" -bad-continuation:106::"Wrong hanging indentation before block (add 4 spaces). - some_arg, # [bad-continuation] - ^ |" -bad-continuation:107::"Wrong hanging indentation before block (add 4 spaces). - some_other_arg): # [bad-continuation] - ^ |" -bad-continuation:127::"Wrong continued indentation (add 3 spaces). - ""b"") # [bad-continuation] - ^ |" -bad-continuation:141::"Wrong hanging indentation before block. - ): pass # [bad-continuation] -| ^|" -bad-continuation:144::"Wrong continued indentation before block (add 4 spaces). - 2): # [bad-continuation] - ^ |" -bad-continuation:152::"Wrong continued indentation (remove 2 spaces). - 2 and # [bad-continuation] - | ^" -bad-continuation:157::"Wrong hanging indentation before block. - 2): pass # [bad-continuation] - ^ | |" -bad-continuation:164::"Wrong continued indentation before block. - 2 or # [bad-continuation] - |^ |" -bad-continuation:168::"Wrong continued indentation before block. - 2): pass # [bad-continuation] - ^ | |" -bad-continuation:174::"Wrong hanging indentation before block. - 2): # [bad-continuation] - ^ | |" -bad-continuation:185::"Wrong continued indentation (add 4 spaces). - 2): # [bad-continuation] - ^ |" diff --git a/tests/functional/b/bad_continuation_py36.py b/tests/functional/b/bad_continuation_py36.py deleted file mode 100644 index 172a9c5a68..0000000000 --- a/tests/functional/b/bad_continuation_py36.py +++ /dev/null @@ -1,8 +0,0 @@ -# pylint: disable=missing-docstring, unused-argument - -async def upload_post( - content: bytes, - source: str, - tags: dict) -> dict: - # regression test for https://github.com/PyCQA/pylint/issues/1415 - raise NotImplementedError('Not implemented') diff --git a/tests/functional/b/bad_continuation_py36.rc b/tests/functional/b/bad_continuation_py36.rc deleted file mode 100644 index 1450ccc640..0000000000 --- a/tests/functional/b/bad_continuation_py36.rc +++ /dev/null @@ -1,2 +0,0 @@ -[testoptions] -min_pyver=3.6 \ No newline at end of file diff --git a/tests/functional/b/bad_continuation_tabs.py b/tests/functional/b/bad_continuation_tabs.py deleted file mode 100644 index a668dc704e..0000000000 --- a/tests/functional/b/bad_continuation_tabs.py +++ /dev/null @@ -1,52 +0,0 @@ -"""Regression test case for bad-continuation with tabs""" -# pylint: disable=too-few-public-methods,missing-docstring,invalid-name,unused-variable, useless-object-inheritance -# Various alignment for brackets - -# Issue 638 -TEST1 = ["foo", - "bar", - "baz" - ] - -MY_LIST = [ - 1, 2, 3, - 4, 5, 6 - ] - -# Issue 1148 -class Abc(object): - def b(self, c): - # bock indentation - self.d( - c) # (is: tabs only) - - def d(self, e): - # bad hanging indentation - self.b( - e) # [bad-continuation] (is: 2 tabs + 7 spaces) - - def f(self): - # block indentiation (is: all tabs only) - return [ - self.b, - self.d - ] - - def g(self): - # bad hanging indentation - # the closing ] requires 7 - 8 more spcaes; see h(), i() - return [self.b, - self.d # (is: 2 tabs + 8 spaces) - ] # [bad-continuation] (is: tabs only) - - def h(self): - # hanging indentation: all lined up with first token 'self.b' - return [self.b, - self.d # (is: 2 tabs + 8 spaces) - ] # (is: 2 tabs + 8 spaces) - - def i(self): - # hangin identation: closing ] lined up with opening [ - return [self.b, - self.d # (2 tabs + 8 spaces) - ] # (2 tabs + 7 spaces) diff --git a/tests/functional/b/bad_continuation_tabs.rc b/tests/functional/b/bad_continuation_tabs.rc deleted file mode 100644 index bebda38099..0000000000 --- a/tests/functional/b/bad_continuation_tabs.rc +++ /dev/null @@ -1,3 +0,0 @@ -[FORMAT] -indent-string='\t' -indent-after-paren=1 diff --git a/tests/functional/b/bad_continuation_tabs.txt b/tests/functional/b/bad_continuation_tabs.txt deleted file mode 100644 index 8fff7bbfb1..0000000000 --- a/tests/functional/b/bad_continuation_tabs.txt +++ /dev/null @@ -1,6 +0,0 @@ -bad-continuation:26::"Wrong hanging indentation (add 1 space). - e) # [bad-continuation] (is: 2 tabs + 7 spaces) - ^|" -bad-continuation:40::"Wrong continued indentation. - ] # [bad-continuation] (is: tabs only) - ^ ||" diff --git a/tests/input/func_bad_cont_dictcomp_py27.py b/tests/input/func_bad_cont_dictcomp_py27.py deleted file mode 100644 index a552710797..0000000000 --- a/tests/input/func_bad_cont_dictcomp_py27.py +++ /dev/null @@ -1,38 +0,0 @@ -"""Bad continuations in dictionary comprehensions.""" - -__revision__ = 0 - -# Dictionary comprehensions should not require extra indentation when breaking -# before the 'for', which is not part of the value -C1 = {'key{}'.format(x): 'value{}'.format(x) - for x in range(3)} - -C2 = {'key{}'.format(x): 'value{}'.format(x) for x in - range(3)} - -# Dictionary comprehensions with multiple loops broken in different places -C3 = {x*y: (x, y) for x in range(3) for y in range(3)} - -C4 = {x*y: (x, y) - for x in range(3) for y in range(3)} - -C5 = {x*y: (x, y) for x - in range(3) for y in range(3)} - -C6 = {x*y: (x, y) for x in range(3) - for y in range(3)} - -C7 = {key: - key ** 2 - for key in range(10)} - -C8 = { - key: key ** 2 - for key in range(10)} - -# Misaligned cases for dict comprehensions -C9 = {'key{}'.format(x): 'value{}'.format(x) - for x in range(3)} # [bad-continuation] - -C9 = {'key{}'.format(x): 'value{}'.format(x) - for x in range(3)} # [bad-continuation] From 9bf40e03fbcbdf3c1a81ca2086abc464e07b89c3 Mon Sep 17 00:00:00 2001 From: Pierre Sassoulas Date: Sat, 2 May 2020 18:05:15 +0200 Subject: [PATCH 2/4] Remove what is directly related to bad-continuation This permits to inline some functions that now have a single instruction. --- pylint/checkers/format.py | 411 +------------------------------------- 1 file changed, 8 insertions(+), 403 deletions(-) diff --git a/pylint/checkers/format.py b/pylint/checkers/format.py index 650650649b..ce789d6883 100644 --- a/pylint/checkers/format.py +++ b/pylint/checkers/format.py @@ -69,16 +69,6 @@ from pylint.utils.pragma_parser import OPTION_PO, PragmaParserError, parse_pragma _ASYNC_TOKEN = "async" -_CONTINUATION_BLOCK_OPENERS = [ - "elif", - "except", - "for", - "if", - "while", - "def", - "class", - "with", -] _KEYWORD_TOKENS = [ "assert", "del", @@ -167,7 +157,6 @@ "Used when an unexpected number of indentation's tabulations or " "spaces has been found.", ), - "C0330": ("Wrong %s indentation%s%s.\n%s%s", "bad-continuation", "TODO"), "W0312": ( "Found indentation with %ss instead of %ss", "mixed-indentation", @@ -249,89 +238,6 @@ def _last_token_on_line_is(tokens, line_end, token): ) -def _token_followed_by_eol(tokens, position): - return ( - tokens.type(position + 1) == tokenize.NL - or tokens.type(position + 1) == tokenize.COMMENT - and tokens.type(position + 2) == tokenize.NL - ) - - -def _get_indent_string(line): - """Return the indention string of the given line.""" - result = "" - for char in line: - if char in " \t": - result += char - else: - break - return result - - -def _get_indent_length(line): - """Return the length of the indentation on the given token's line.""" - result = 0 - for char in line: - if char == " ": - result += 1 - elif char == "\t": - result += _TAB_LENGTH - else: - break - return result - - -def _get_indent_hint_line(bar_positions, bad_position): - """Return a line with |s for each of the positions in the given lists.""" - if not bar_positions: - return "", "" - - bar_positions = [_get_indent_length(indent) for indent in bar_positions] - bad_position = _get_indent_length(bad_position) - delta_message = "" - markers = [(pos, "|") for pos in bar_positions] - if len(markers) == 1: - # if we have only one marker we'll provide an extra hint on how to fix - expected_position = markers[0][0] - delta = abs(expected_position - bad_position) - direction = "add" if expected_position > bad_position else "remove" - delta_message = _CONTINUATION_HINT_MESSAGE % ( - direction, - delta, - "s" if delta > 1 else "", - ) - markers.append((bad_position, "^")) - markers.sort() - line = [" "] * (markers[-1][0] + 1) - for position, marker in markers: - line[position] = marker - return "".join(line), delta_message - - -class _ContinuedIndent: - __slots__ = ( - "valid_outdent_strings", - "valid_continuation_strings", - "context_type", - "token", - "position", - ) - - def __init__( - self, - context_type, - token, - position, - valid_outdent_strings, - valid_continuation_strings, - ): - self.valid_outdent_strings = valid_outdent_strings - self.valid_continuation_strings = valid_continuation_strings - self.context_type = context_type - self.position = position - self.token = token - - # The contexts for hanging indents. # A hanging indented dictionary value after : HANGING_DICT_VALUE = "dict-value" @@ -347,34 +253,6 @@ def __init__( SINGLE_LINE = "single" WITH_BODY = "multi" -_CONTINUATION_MSG_PARTS = { - HANGING_DICT_VALUE: ("hanging", " in dict value"), - HANGING: ("hanging", ""), - HANGING_BLOCK: ("hanging", " before block"), - CONTINUED: ("continued", ""), - CONTINUED_BLOCK: ("continued", " before block"), -} - -_CONTINUATION_HINT_MESSAGE = " (%s %d space%s)" # Ex: (remove 2 spaces) - - -def _Indentations(*args): - """Valid indentation strings for a continued line.""" - return {a: None for a in args} - - -def _BeforeBlockIndentations(single, with_body): - """Valid alternative indentation strings for continued lines before blocks. - - :param int single: Valid indentation string for statements on a single logical line. - :param int with_body: Valid indentation string for statements on several lines. - - :returns: A dictionary mapping indent offsets to a string representing - whether the indent if for a line or block. - :rtype: dict - """ - return {single: SINGLE_LINE, with_body: WITH_BODY} - class TokenWrapper: """A wrapper for readable access to token information.""" @@ -397,195 +275,6 @@ def start_col(self, idx): def line(self, idx): return self._tokens[idx][4] - def line_indent(self, idx): - """Get the string of TABs and Spaces used for indentation of the line of this token""" - return _get_indent_string(self.line(idx)) - - def token_indent(self, idx): - """Get an indentation string for hanging indentation, consisting of the line-indent plus - a number of spaces to fill up to the column of this token. - - e.g. the token indent for foo - in "print(foo)" - is " " - """ - line_indent = self.line_indent(idx) - return line_indent + " " * (self.start_col(idx) - len(line_indent)) - - -class ContinuedLineState: - """Tracker for continued indentation inside a logical line.""" - - def __init__(self, tokens, config): - self._line_start = -1 - self._cont_stack = [] - self._is_block_opener = False - self.retained_warnings = [] - self._config = config - self._tokens = TokenWrapper(tokens) - - @property - def has_content(self): - return bool(self._cont_stack) - - @property - def _block_indent_string(self): - return self._config.indent_string.replace("\\t", "\t") - - @property - def _continuation_string(self): - return self._block_indent_string[0] * self._config.indent_after_paren - - @property - def _continuation_size(self): - return self._config.indent_after_paren - - def handle_line_start(self, pos): - """Record the first non-junk token at the start of a line.""" - if self._line_start > -1: - return - - check_token_position = pos - if self._tokens.token(pos) == _ASYNC_TOKEN: - check_token_position += 1 - self._is_block_opener = ( - self._tokens.token(check_token_position) in _CONTINUATION_BLOCK_OPENERS - ) - self._line_start = pos - - def next_physical_line(self): - """Prepares the tracker for a new physical line (NL).""" - self._line_start = -1 - self._is_block_opener = False - - def next_logical_line(self): - """Prepares the tracker for a new logical line (NEWLINE). - - A new logical line only starts with block indentation. - """ - self.next_physical_line() - self.retained_warnings = [] - self._cont_stack = [] - - def add_block_warning(self, token_position, state, valid_indentations): - self.retained_warnings.append((token_position, state, valid_indentations)) - - def get_valid_indentations(self, idx): - """Returns the valid offsets for the token at the given position.""" - # The closing brace on a dict or the 'for' in a dict comprehension may - # reset two indent levels because the dict value is ended implicitly - stack_top = -1 - if ( - self._tokens.token(idx) in ("}", "for") - and self._cont_stack[-1].token == ":" - ): - stack_top = -2 - indent = self._cont_stack[stack_top] - if self._tokens.token(idx) in _CLOSING_BRACKETS: - valid_indentations = indent.valid_outdent_strings - else: - valid_indentations = indent.valid_continuation_strings - return indent, valid_indentations.copy() - - def _hanging_indent_after_bracket(self, bracket, position): - """Extracts indentation information for a hanging indent - - Case of hanging indent after a bracket (including parenthesis) - - :param str bracket: bracket in question - :param int position: Position of bracket in self._tokens - - :returns: the state and valid positions for hanging indentation - :rtype: _ContinuedIndent - """ - indentation = self._tokens.line_indent(position) - if ( - self._is_block_opener - and self._continuation_string == self._block_indent_string - ): - return _ContinuedIndent( - HANGING_BLOCK, - bracket, - position, - _Indentations(indentation + self._continuation_string, indentation), - _BeforeBlockIndentations( - indentation + self._continuation_string, - indentation + self._continuation_string * 2, - ), - ) - if bracket == ":": - # If the dict key was on the same line as the open brace, the new - # correct indent should be relative to the key instead of the - # current indent level - paren_align = self._cont_stack[-1].valid_outdent_strings - next_align = self._cont_stack[-1].valid_continuation_strings.copy() - next_align_keys = list(next_align.keys()) - next_align[next_align_keys[0] + self._continuation_string] = True - # Note that the continuation of - # d = { - # 'a': 'b' - # 'c' - # } - # is handled by the special-casing for hanging continued string indents. - return _ContinuedIndent( - HANGING_DICT_VALUE, bracket, position, paren_align, next_align - ) - return _ContinuedIndent( - HANGING, - bracket, - position, - _Indentations(indentation, indentation + self._continuation_string), - _Indentations(indentation + self._continuation_string), - ) - - def _continuation_inside_bracket(self, bracket, position): - """Extracts indentation information for a continued indent.""" - indentation = self._tokens.line_indent(position) - token_indent = self._tokens.token_indent(position) - next_token_indent = self._tokens.token_indent(position + 1) - if ( - self._is_block_opener - and next_token_indent == indentation + self._block_indent_string - ): - return _ContinuedIndent( - CONTINUED_BLOCK, - bracket, - position, - _Indentations(token_indent), - _BeforeBlockIndentations( - next_token_indent, next_token_indent + self._continuation_string - ), - ) - return _ContinuedIndent( - CONTINUED, - bracket, - position, - _Indentations(token_indent, next_token_indent), - _Indentations(next_token_indent), - ) - - def pop_token(self): - self._cont_stack.pop() - - def push_token(self, token, position): - """Pushes a new token for continued indentation on the stack. - - Tokens that can modify continued indentation offsets are: - * opening brackets - * 'lambda' - * : inside dictionaries - - push_token relies on the caller to filter out those - interesting tokens. - - :param int token: The concrete token - :param int position: The position of the token in the stream. - """ - if _token_followed_by_eol(self._tokens, position): - self._cont_stack.append(self._hanging_indent_after_bracket(token, position)) - else: - self._cont_stack.append(self._continuation_inside_bracket(token, position)) - class FormatChecker(BaseTokenChecker): """checks for : @@ -716,14 +405,6 @@ def __init__(self, linter=None): self._visited_lines = None self._bracket_stack = [None] - def _pop_token(self): - self._bracket_stack.pop() - self._current_line.pop_token() - - def _push_token(self, token, idx): - self._bracket_stack.append(token) - self._current_line.push_token(token, idx) - def new_line(self, tokens, line_end, line_start): """a new line has been encountered, process it if necessary""" if _last_token_on_line_is(tokens, line_end, ";"): @@ -751,7 +432,7 @@ def _check_keyword_parentheses(self, tokens, start): """ # If the next token is not a paren, we're fine. if self._inside_brackets(":") and tokens[start][1] == "for": - self._pop_token() + self._bracket_stack.pop() if tokens[start + 1][1] != "(": return @@ -815,7 +496,7 @@ def _check_keyword_parentheses(self, tokens, start): return def _opening_bracket(self, tokens, i): - self._push_token(tokens[i][1], i) + self._bracket_stack.append(tokens[i][1]) # Special case: ignore slices if tokens[i][1] == "[" and tokens[i + 1][1] == ":": return @@ -831,8 +512,8 @@ def _opening_bracket(self, tokens, i): def _closing_bracket(self, tokens, i): if self._inside_brackets(":"): - self._pop_token() - self._pop_token() + self._bracket_stack.pop() + self._bracket_stack.pop() # Special case: ignore slices if tokens[i - 1][1] == ":" and tokens[i][1] == "]": return @@ -879,7 +560,7 @@ def _check_equals_spacing(self, tokens, i): self._check_space(tokens, i, (_MUST, _MUST)) def _open_lambda(self, tokens, i): # pylint:disable=unused-argument - self._push_token("lambda", i) + self._bracket_stack.append("lambda") def _handle_colon(self, tokens, i): # Special case: ignore slices @@ -892,9 +573,9 @@ def _handle_colon(self, tokens, i): self._check_space(tokens, i, policy) if self._inside_brackets("lambda"): - self._pop_token() + self._bracket_stack.pop() elif self._inside_brackets("{"): - self._push_token(":", i) + self._bracket_stack.append(":") def _handle_comma(self, tokens, i): # Only require a following whitespace if this is @@ -904,7 +585,7 @@ def _handle_comma(self, tokens, i): else: self._check_space(tokens, i, (_MUST_NOT, _MUST)) if self._inside_brackets(":"): - self._pop_token() + self._bracket_stack.pop() def _check_surrounded_by_space(self, tokens, i): """Check that a binary operator is surrounded by exactly one space.""" @@ -985,8 +666,6 @@ def _prepare_token_dispatcher(self): def process_tokens(self, tokens): """process tokens and search for : - _ non strict indentation (i.e. not always using the parameter as - indent unit) _ too long lines (i.e. longer than ) _ optionally bad construct (if given, bad_construct must be a compiled regular expression). @@ -1000,8 +679,6 @@ def process_tokens(self, tokens): token_handlers = self._prepare_token_dispatcher() self._last_line_ending = None last_blank_line_num = 0 - - self._current_line = ContinuedLineState(tokens, self.config) for idx, (tok_type, token, start, _, line) in enumerate(tokens): if start[0] != line_num: line_num = start[0] @@ -1020,8 +697,6 @@ def process_tokens(self, tokens): # If an INDENT appears, setting check_equal is wrong, and will # be undone when we see the INDENT. 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 @@ -1038,10 +713,7 @@ def process_tokens(self, tokens): elif tok_type == tokenize.NL: if not line.strip("\r\n"): last_blank_line_num = line_num - self._check_continued_indentation(TokenWrapper(tokens), idx + 1) - self._current_line.next_physical_line() elif tok_type not in (tokenize.COMMENT, tokenize.ENCODING): - self._current_line.handle_line_start(idx) # This is the first concrete token following a NEWLINE, so it # must be the first token of the next program statement, or an # ENDMARKER; the "line" argument exposes the leading whitespace @@ -1106,73 +778,6 @@ def _check_line_ending(self, line_ending, line_num): 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, indentations in self._current_line.retained_warnings: - block_type = indentations[tokens.token_indent(indent_pos)] - hints = {k: v for k, v in indentations.items() if v != block_type} - if single_line_block_stmt and block_type == WITH_BODY: - self._add_continuation_message(state, hints, tokens, indent_pos) - elif not single_line_block_stmt and block_type == SINGLE_LINE: - self._add_continuation_message(state, hints, tokens, indent_pos) - - def _check_continued_indentation(self, tokens, next_idx): - def same_token_around_nl(token_type): - return ( - tokens.type(next_idx) == token_type - and tokens.type(next_idx - 2) == token_type - ) - - # Do not issue any warnings if the next line is empty. - if not self._current_line.has_content or tokens.type(next_idx) == tokenize.NL: - return - - state, valid_indentations = self._current_line.get_valid_indentations(next_idx) - # Special handling for hanging comments and strings. If the last line ended - # with a comment (string) and the new line contains only a comment, the line - # may also be indented to the start of the previous token. - if same_token_around_nl(tokenize.COMMENT) or same_token_around_nl( - tokenize.STRING - ): - valid_indentations[tokens.token_indent(next_idx - 2)] = True - - # We can only decide if the indentation of a continued line before opening - # a new block is valid once we know of the body of the block is on the - # same line as the block opener. Since the token processing is single-pass, - # emitting those warnings is delayed until the block opener is processed. - if ( - state.context_type in (HANGING_BLOCK, CONTINUED_BLOCK) - and tokens.token_indent(next_idx) in valid_indentations - ): - self._current_line.add_block_warning(next_idx, state, valid_indentations) - elif tokens.token_indent(next_idx) not in valid_indentations: - length_indentation = len(tokens.token_indent(next_idx)) - if not any( - length_indentation == 2 * len(indentation) - for indentation in valid_indentations - ): - self._add_continuation_message( - state, valid_indentations, tokens, next_idx - ) - - def _add_continuation_message(self, state, indentations, tokens, position): - readable_type, readable_position = _CONTINUATION_MSG_PARTS[state.context_type] - hint_line, delta_message = _get_indent_hint_line( - indentations, tokens.token_indent(position) - ) - self.add_message( - "bad-continuation", - line=tokens.start_line(position), - args=( - readable_type, - readable_position, - delta_message, - tokens.line(position), - hint_line, - ), - ) - @check_messages("multiple-statements") def visit_default(self, node): """check the node line number and check it if not yet done""" From 7fe8b9effea868961cc86d631bc83e4ddeae7fb8 Mon Sep 17 00:00:00 2001 From: Pierre Sassoulas Date: Sat, 2 May 2020 17:54:55 +0200 Subject: [PATCH 3/4] Remove bad-continuation disabling in all code --- pylint/lint/run.py | 1 - tests/functional/i/implicit_str_concat.py | 2 +- tests/functional/i/implicit_str_concat_latin1.py | 2 +- tests/functional/i/implicit_str_concat_multiline.py | 2 +- tests/functional/i/implicit_str_concat_utf8.py | 2 +- tests/functional/t/trailing_comma_tuple.py | 2 +- 6 files changed, 5 insertions(+), 6 deletions(-) diff --git a/pylint/lint/run.py b/pylint/lint/run.py index 1f92e68af7..7044805699 100644 --- a/pylint/lint/run.py +++ b/pylint/lint/run.py @@ -252,7 +252,6 @@ def __init__( linter.load_plugin_modules(self._plugins) # add some help section linter.add_help_section("Environment variables", config.ENV_HELP, level=1) - # pylint: disable=bad-continuation linter.add_help_section( "Output", "Using the default text output, the message format is : \n" diff --git a/tests/functional/i/implicit_str_concat.py b/tests/functional/i/implicit_str_concat.py index 9d4a908f5c..707fa75bf4 100644 --- a/tests/functional/i/implicit_str_concat.py +++ b/tests/functional/i/implicit_str_concat.py @@ -1,4 +1,4 @@ -#pylint: disable=bad-continuation,invalid-name,missing-docstring +#pylint: disable=invalid-name,missing-docstring # Basic test with a list TEST_LIST1 = ['a' 'b'] # [implicit-str-concat] diff --git a/tests/functional/i/implicit_str_concat_latin1.py b/tests/functional/i/implicit_str_concat_latin1.py index 44a2a94cb2..cf8c3e6ab8 100644 --- a/tests/functional/i/implicit_str_concat_latin1.py +++ b/tests/functional/i/implicit_str_concat_latin1.py @@ -1,4 +1,4 @@ # coding: latin_1 -#pylint: disable=bad-continuation,invalid-name,missing-docstring +#pylint: disable=invalid-name,missing-docstring TOTO = ('Café', 'Café', 'Café') diff --git a/tests/functional/i/implicit_str_concat_multiline.py b/tests/functional/i/implicit_str_concat_multiline.py index 7bc6dfb836..337ac80353 100755 --- a/tests/functional/i/implicit_str_concat_multiline.py +++ b/tests/functional/i/implicit_str_concat_multiline.py @@ -1,4 +1,4 @@ -#pylint: disable=bad-continuation,missing-docstring +#pylint: disable=missing-docstring TEST_TUPLE = ('a', 'b' # [implicit-str-concat] 'c') diff --git a/tests/functional/i/implicit_str_concat_utf8.py b/tests/functional/i/implicit_str_concat_utf8.py index fa996e90cc..7cd6618602 100644 --- a/tests/functional/i/implicit_str_concat_utf8.py +++ b/tests/functional/i/implicit_str_concat_utf8.py @@ -1,3 +1,3 @@ -#pylint: disable=bad-continuation,invalid-name,missing-docstring +#pylint: disable=invalid-name,missing-docstring TOTO = ('CafĂ©', 'CafĂ©', 'CafĂ©') diff --git a/tests/functional/t/trailing_comma_tuple.py b/tests/functional/t/trailing_comma_tuple.py index 322317030c..a5bfcf49be 100644 --- a/tests/functional/t/trailing_comma_tuple.py +++ b/tests/functional/t/trailing_comma_tuple.py @@ -1,5 +1,5 @@ """Check trailing comma one element tuples.""" -# pylint: disable=bad-whitespace, missing-docstring, bad-continuation +# pylint: disable=bad-whitespace, missing-docstring AAA = 1, # [trailing-comma-tuple] BBB = "aaaa", # [trailing-comma-tuple] CCC="aaa", # [trailing-comma-tuple] From 5ddea68cc055e6174edd8b84b8e5726f3ea04c62 Mon Sep 17 00:00:00 2001 From: Pierre Sassoulas Date: Sat, 2 May 2020 18:58:20 +0200 Subject: [PATCH 4/4] Update documentation for bad-continuation removal --- ChangeLog | 4 ++++ doc/whatsnew/2.6.rst | 2 ++ 2 files changed, 6 insertions(+) diff --git a/ChangeLog b/ChangeLog index fafae7a41c..5cd3f44830 100644 --- a/ChangeLog +++ b/ChangeLog @@ -34,6 +34,10 @@ Release date: TBA Close #3528 +* bad-continuation has been removed, black or another formatter can help you with this better than Pylint + +Close #289, #638, #747, #1148, #1179, #1943, #2301, #2304, #2944, #3565 + What's New in Pylint 2.5.0? =========================== diff --git a/doc/whatsnew/2.6.rst b/doc/whatsnew/2.6.rst index 6fe33126ab..eb18da612b 100644 --- a/doc/whatsnew/2.6.rst +++ b/doc/whatsnew/2.6.rst @@ -15,3 +15,5 @@ New checkers Other Changes ============= + +* `bad-continuation` has been removed, `black` or another formatter can help you with this better than Pylint