Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remove bad whitespace from the codebase #3577

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
11 changes: 7 additions & 4 deletions ChangeLog
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,13 @@ What's New in Pylint 2.6.0?
===========================
Release date: TBA

* bad-continuation and bad-whitespace have been removed, black or another formatter can help you with this better than Pylint

Close #246, #289, #638, #747, #1148, #1179, #1943, #2041, #2301, #2304, #2944, #3565

* The no-space-check option has been removed. It's no longer possible to consider empty line like a `trailing-whitespace` by using clever options

Close #1368

What's New in Pylint 2.5.1?
===========================
Expand Down Expand Up @@ -34,10 +41,6 @@ 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?
===========================

Expand Down
4 changes: 3 additions & 1 deletion doc/whatsnew/2.6.rst
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,6 @@ New checkers
Other Changes
=============

* `bad-continuation` has been removed, `black` or another formatter can help you with this better than Pylint
* `bad-continuation` and `bad-whitespace` have been removed. `black` or another formatter can help you with this better than Pylint

* The `no-space-check` option has been removed, it's no longer possible to consider empty line like a `trailing-whitespace` by using clever options.
249 changes: 11 additions & 238 deletions pylint/checkers/format.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,6 @@
Some parts of the process_token method is based from The Tab Nanny std module.
"""

import keyword
import tokenize
from functools import reduce # pylint: disable=redefined-builtin
from typing import List
Expand Down Expand Up @@ -118,13 +117,6 @@
_MUST_NOT = 1
_IGNORE = 2

# Whitespace checking config constants
_DICT_SEPARATOR = "dict-separator"
_TRAILING_COMMA = "trailing-comma"
_EMPTY_LINE = "empty-line"
_NO_SPACE_CHECK_CHOICES = [_TRAILING_COMMA, _DICT_SEPARATOR, _EMPTY_LINE]
_DEFAULT_NO_SPACE_CHECK_CHOICES = [_TRAILING_COMMA, _DICT_SEPARATOR]

MSGS = {
"C0301": (
"Line too long (%s/%s)",
Expand Down Expand Up @@ -180,21 +172,6 @@
"Used when a single item in parentheses follows an if, for, or "
"other keyword.",
),
"C0326": (
"%s space %s %s %s\n%s",
"bad-whitespace",
(
"Used when a wrong number of spaces is used around an operator, "
"bracket or block opener."
),
{
"old_names": [
("C0323", "no-space-after-operator"),
("C0324", "no-space-after-comma"),
("C0322", "no-space-before-operator"),
]
},
),
"C0327": (
"Mixed line endings LF and CRLF",
"mixed-line-endings",
Expand All @@ -208,26 +185,6 @@
}


def _underline_token(token):
length = token[3][1] - token[2][1]
offset = token[2][1]
referenced_line = token[4]
# If the referenced line does not end with a newline char, fix it
if referenced_line[-1] != "\n":
referenced_line += "\n"
return referenced_line + (" " * offset) + ("^" * length)


def _column_distance(token1, token2):
if token1 == token2:
return 0
if token2[3] < token1[3]:
token1, token2 = token2, token1
if token1[3][0] != token2[2][0]:
return None
return token2[2][1] - token1[3][1]


def _last_token_on_line_is(tokens, line_end, token):
return (
line_end > 0
Expand Down Expand Up @@ -337,24 +294,6 @@ class FormatChecker(BaseTokenChecker):
),
},
),
(
"no-space-check",
{
"default": ",".join(_DEFAULT_NO_SPACE_CHECK_CHOICES),
"metavar": ",".join(_NO_SPACE_CHECK_CHOICES),
"type": "multiple_choice",
"choices": _NO_SPACE_CHECK_CHOICES,
"help": (
"List of optional constructs for which whitespace "
"checking is disabled. "
"`" + _DICT_SEPARATOR + "` is used to allow tabulation "
"in dicts, etc.: {1 : 1,\\n222: 2}. "
"`" + _TRAILING_COMMA + "` allows a space between comma "
"and closing bracket: (a, ). "
"`" + _EMPTY_LINE + "` allows space-only lines."
),
},
),
(
"max-module-lines",
{
Expand Down Expand Up @@ -431,23 +370,19 @@ def _check_keyword_parentheses(self, tokens, start):
start: int; the position of the keyword in the token list.
"""
# If the next token is not a paren, we're fine.
if self._inside_brackets(":") and tokens[start][1] == "for":
if self._bracket_stack[-1] == ":" and tokens[start][1] == "for":
self._bracket_stack.pop()
if tokens[start + 1][1] != "(":
return

found_and_or = False
depth = 0
keyword_token = str(tokens[start][1])
line_num = tokens[start][2][0]

for i in range(start, len(tokens) - 1):
token = tokens[i]

# If we hit a newline, then assume any parens were for continuation.
if token[0] == tokenize.NL:
return

if token[1] == "(":
depth += 1
elif token[1] == ")":
Expand Down Expand Up @@ -495,170 +430,11 @@ def _check_keyword_parentheses(self, tokens, start):
elif token[1] == "for":
return

def _opening_bracket(self, tokens, i):
self._bracket_stack.append(tokens[i][1])
# Special case: ignore slices
if tokens[i][1] == "[" and tokens[i + 1][1] == ":":
return

if i > 0 and (
tokens[i - 1][0] == tokenize.NAME
and not (keyword.iskeyword(tokens[i - 1][1]))
or tokens[i - 1][1] in _CLOSING_BRACKETS
):
self._check_space(tokens, i, (_MUST_NOT, _MUST_NOT))
else:
self._check_space(tokens, i, (_IGNORE, _MUST_NOT))

def _closing_bracket(self, tokens, i):
if self._inside_brackets(":"):
self._bracket_stack.pop()
self._bracket_stack.pop()
# Special case: ignore slices
if tokens[i - 1][1] == ":" and tokens[i][1] == "]":
return
policy_before = _MUST_NOT
if tokens[i][1] in _CLOSING_BRACKETS and tokens[i - 1][1] == ",":
if _TRAILING_COMMA in self.config.no_space_check:
policy_before = _IGNORE

self._check_space(tokens, i, (policy_before, _IGNORE))

def _has_valid_type_annotation(self, tokens, i):
"""Extended check of PEP-484 type hint presence"""
if not self._inside_brackets("("):
return False
# token_info
# type string start end line
# 0 1 2 3 4
bracket_level = 0
for token in tokens[i - 1 :: -1]:
if token[1] == ":":
return True
if token[1] == "(":
return False
if token[1] == "]":
bracket_level += 1
elif token[1] == "[":
bracket_level -= 1
elif token[1] == ",":
if not bracket_level:
return False
elif token[1] in (".", "..."):
continue
elif token[0] not in (tokenize.NAME, tokenize.STRING, tokenize.NL):
return False
return False

def _check_equals_spacing(self, tokens, i):
"""Check the spacing of a single equals sign."""
if self._has_valid_type_annotation(tokens, i):
self._check_space(tokens, i, (_MUST, _MUST))
elif self._inside_brackets("(") or self._inside_brackets("lambda"):
self._check_space(tokens, i, (_MUST_NOT, _MUST_NOT))
else:
self._check_space(tokens, i, (_MUST, _MUST))

def _open_lambda(self, tokens, i): # pylint:disable=unused-argument
self._bracket_stack.append("lambda")

def _handle_colon(self, tokens, i):
# Special case: ignore slices
if self._inside_brackets("["):
return
if self._inside_brackets("{") and _DICT_SEPARATOR in self.config.no_space_check:
policy = (_IGNORE, _IGNORE)
else:
policy = (_MUST_NOT, _MUST)
self._check_space(tokens, i, policy)

if self._inside_brackets("lambda"):
self._bracket_stack.pop()
elif self._inside_brackets("{"):
self._bracket_stack.append(":")

def _handle_comma(self, tokens, i):
# Only require a following whitespace if this is
# not a hanging comma before a closing bracket.
if tokens[i + 1][1] in _CLOSING_BRACKETS:
self._check_space(tokens, i, (_MUST_NOT, _IGNORE))
else:
self._check_space(tokens, i, (_MUST_NOT, _MUST))
if self._inside_brackets(":"):
self._bracket_stack.pop()

def _check_surrounded_by_space(self, tokens, i):
"""Check that a binary operator is surrounded by exactly one space."""
self._check_space(tokens, i, (_MUST, _MUST))

def _check_space(self, tokens, i, policies):
def _policy_string(policy):
if policy == _MUST:
return "Exactly one", "required"
return "No", "allowed"

def _name_construct(token):
if token[1] == ",":
return "comma"
if token[1] == ":":
return ":"
if token[1] in "()[]{}":
return "bracket"
if token[1] in ("<", ">", "<=", ">=", "!=", "=="):
return "comparison"
if self._inside_brackets("("):
return "keyword argument assignment"
return "assignment"

good_space = [True, True]
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:
continue

distance = _column_distance(*token_pair)
if distance is None:
continue
good_space[other_idx] = (policy == _MUST and distance == 1) or (
policy == _MUST_NOT and distance == 0
)

warnings = []
if not any(good_space) and policies[0] == policies[1]:
warnings.append((policies[0], "around"))
else:
for ok, policy, position in zip(good_space, policies, ("before", "after")):
if not ok:
warnings.append((policy, position))
for policy, position in warnings:
construct = _name_construct(token)
count, state = _policy_string(policy)
self.add_message(
"bad-whitespace",
line=token[2][0],
args=(count, state, position, construct, _underline_token(token)),
col_offset=token[2][1],
)

def _inside_brackets(self, left):
return self._bracket_stack[-1] == left

def _prepare_token_dispatcher(self):
raw = [
(_KEYWORD_TOKENS, self._check_keyword_parentheses),
(_OPENING_BRACKETS, self._opening_bracket),
(_CLOSING_BRACKETS, self._closing_bracket),
(["="], self._check_equals_spacing),
(_SPACED_OPERATORS, self._check_surrounded_by_space),
([","], self._handle_comma),
([":"], self._handle_colon),
(["lambda"], self._open_lambda),
]

dispatch = {}
for tokens, handler in raw:
for tokens, handler in [
(_KEYWORD_TOKENS, self._check_keyword_parentheses),
]:
for token in tokens:
dispatch[token] = handler
return dispatch
Expand Down Expand Up @@ -861,16 +637,13 @@ def check_line_ending(self, line: str, i: int) -> None:
"""
if not line.endswith("\n"):
self.add_message("missing-final-newline", line=i)
else:
# exclude \f (formfeed) from the rstrip
stripped_line = line.rstrip("\t\n\r\v ")
if not stripped_line and _EMPTY_LINE in self.config.no_space_check:
# allow empty lines
pass
elif line[len(stripped_line) :] not in ("\n", "\r\n"):
self.add_message(
"trailing-whitespace", line=i, col_offset=len(stripped_line)
)
return
# exclude \f (formfeed) from the rstrip
stripped_line = line.rstrip("\t\n\r\v ")
if line[len(stripped_line) :] not in ("\n", "\r\n"):
self.add_message(
"trailing-whitespace", line=i, col_offset=len(stripped_line)
)

def check_line_length(self, line: str, i: int) -> None:
"""
Expand Down