summaryrefslogtreecommitdiff
path: root/pylint/checkers/format.py
diff options
context:
space:
mode:
Diffstat (limited to 'pylint/checkers/format.py')
-rw-r--r--pylint/checkers/format.py134
1 files changed, 87 insertions, 47 deletions
diff --git a/pylint/checkers/format.py b/pylint/checkers/format.py
index e297d3886..0463bcc91 100644
--- a/pylint/checkers/format.py
+++ b/pylint/checkers/format.py
@@ -46,6 +46,7 @@ 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
from astroid import nodes
@@ -56,8 +57,9 @@ from pylint.checkers.utils import (
is_protocol_class,
node_frame_class,
)
-from pylint.constants import OPTION_RGX, WarningScope
+from pylint.constants import WarningScope
from pylint.interfaces import IAstroidChecker, IRawChecker, ITokenChecker
+from pylint.utils.pragma_parser import OPTION_PO, PragmaParserError, parse_pragma
_ASYNC_TOKEN = "async"
_CONTINUATION_BLOCK_OPENERS = [
@@ -1243,41 +1245,64 @@ class FormatChecker(BaseTokenChecker):
self.add_message("multiple-statements", node=node)
self._visited_lines[line] = 2
- def check_lines(self, lines, i):
- """check lines have less than a maximum number of characters
+ def check_line_ending(self, line: str, i: int) -> None:
+ """
+ Check that the final newline is not missing and that there is no trailing whitespace.
+ """
+ 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)
+ )
+
+ def check_line_length(self, line: str, i: int) -> None:
+ """
+ Check that the line length is less than the authorized value
"""
max_chars = self.config.max_line_length
ignore_long_line = self.config.ignore_long_lines
+ line = line.rstrip()
+ if len(line) > max_chars and not ignore_long_line.search(line):
+ self.add_message("line-too-long", line=i, args=(len(line), max_chars))
- def check_line(line, i):
- 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)
- )
- # Don't count excess whitespace in the line length.
- line = stripped_line
- mobj = OPTION_RGX.search(line)
- if mobj and "=" in line:
- front_of_equal, _, back_of_equal = mobj.group(1).partition("=")
- if front_of_equal.strip() == "disable":
- if "line-too-long" in {
- _msg_id.strip() for _msg_id in back_of_equal.split(",")
- }:
- return None
- line = line.rsplit("#", 1)[0].rstrip()
-
- if len(line) > max_chars and not ignore_long_line.search(line):
- self.add_message("line-too-long", line=i, args=(len(line), max_chars))
- return i + 1
+ @staticmethod
+ def remove_pylint_option_from_lines(options_pattern_obj) -> str:
+ """
+ Remove the `# pylint ...` pattern from lines
+ """
+ lines = options_pattern_obj.string
+ purged_lines = (
+ lines[: options_pattern_obj.start(1)].rstrip()
+ + lines[options_pattern_obj.end(1) :]
+ )
+ return purged_lines
+ @staticmethod
+ def is_line_length_check_activated(pylint_pattern_match_object) -> bool:
+ """
+ Return true if the line length check is activated
+ """
+ try:
+ for pragma in parse_pragma(pylint_pattern_match_object.group(2)):
+ if pragma.action == "disable" and "line-too-long" in pragma.messages:
+ return False
+ except PragmaParserError:
+ # Printing usefull informations dealing with this error is done in lint.py
+ pass
+ return True
+
+ @staticmethod
+ def specific_splitlines(lines: str) -> List[str]:
+ """
+ Split lines according to universal newlines except those in a specific sets
+ """
unsplit_ends = {
"\v",
"\x0b",
@@ -1290,23 +1315,38 @@ class FormatChecker(BaseTokenChecker):
"\u2028",
"\u2029",
}
- unsplit = []
- for line in lines.splitlines(True):
- if line[-1] in unsplit_ends:
- unsplit.append(line)
- continue
-
- if unsplit:
- unsplit.append(line)
- line = "".join(unsplit)
- unsplit = []
-
- i = check_line(line, i)
- if i is None:
- break
+ res = []
+ buffer = ""
+ for atomic_line in lines.splitlines(True):
+ if atomic_line[-1] not in unsplit_ends:
+ res.append(buffer + atomic_line)
+ buffer = ""
+ else:
+ buffer += atomic_line
+ return res
- if unsplit:
- check_line("".join(unsplit), i)
+ def check_lines(self, lines: str, lineno: int) -> None:
+ """
+ Check lines have :
+ - a final newline
+ - no trailing whitespace
+ - less than a maximum number of characters
+ """
+ #  By default, check the line length
+ check_l_length = True
+
+ # Line length check may be deactivated through `pylint: disable` comment
+ mobj = OPTION_PO.search(lines)
+ if mobj:
+ check_l_length = self.is_line_length_check_activated(mobj)
+ # The 'pylint: disable whatever' should not be taken into account for line length count
+ lines = self.remove_pylint_option_from_lines(mobj)
+
+ for line in self.specific_splitlines(lines):
+ if check_l_length:
+ self.check_line_length(line, lineno)
+ self.check_line_ending(line, lineno)
+ lineno += 1
def check_indent_level(self, string, expected, line_num):
"""return the indent level of the string