diff options
-rw-r--r-- | ChangeLog | 10 | ||||
-rw-r--r-- | pylint/checkers/spelling.py | 24 | ||||
-rw-r--r-- | tests/checkers/unittest_spelling.py | 72 |
3 files changed, 72 insertions, 34 deletions
@@ -67,6 +67,14 @@ Release date: Undefined * Don't show ``DuplicateBasesError`` for attribute access +* Allow code flanked in backticks to be skipped by spellchecker + + Closes #4319 + +* Allow Python tool directives (for black, flake8, zimports, isort, mypy, bandit, pycharm) at beginning of comments to be skipped by spellchecker + + Closes #4320 + What's New in Pylint 2.7.4? =========================== @@ -302,7 +310,7 @@ Release date: 2021-02-21 Close #2738 -* Fix ``duplicate-code`` false positive when lines only contain whitespace and non-alphanumeric characters (e.g. parentheses, bracket, comman, etc.) +* Fix ``duplicate-code`` false positive when lines only contain whitespace and non-alphanumeric characters (e.g. parentheses, bracket, comma, etc.) * Improve lint message for ``singleton-comparison`` with bools diff --git a/pylint/checkers/spelling.py b/pylint/checkers/spelling.py index dca7da3b5..b08ec772a 100644 --- a/pylint/checkers/spelling.py +++ b/pylint/checkers/spelling.py @@ -103,7 +103,7 @@ class WordsWithUnderscores(Filter): class RegExFilter(Filter): r"""Parent class for filters using regular expressions. - This filter skips any words the match the expression assigned to the class attribute `_pattern` + This filter skips any words the match the expression assigned to the class attribute ``_pattern`` """ _pattern: Pattern[str] @@ -176,6 +176,24 @@ class ForwardSlashChunker(Chunker): raise StopIteration() +CODE_FLANKED_IN_BACKTICK_REGEX = re.compile(r"(\s|^)(`{1,2})([^`]+)(\2)([^`]|$)") + + +def _strip_code_flanked_in_backticks(line: str) -> str: + """Alter line so code flanked in backticks is ignored. + + Pyenchant automatically strips backticks when parsing tokens, so this cannot be done at the individual filter level. + + """ + + def replace_code_but_leave_surrounding_characters(match_obj) -> str: + return match_obj.group(1) + match_obj.group(5) + + return CODE_FLANKED_IN_BACKTICK_REGEX.sub( + replace_code_but_leave_surrounding_characters, line + ) + + class SpellingChecker(BaseTokenChecker): """Check spelling in comments and docstrings""" @@ -323,6 +341,7 @@ class SpellingChecker(BaseTokenChecker): "noqa", "nosec", "isort:skip", + "mypy:", ): if line.startswith(" " + iter_directive): line = line[(len(iter_directive) + 1) :] @@ -330,6 +349,9 @@ class SpellingChecker(BaseTokenChecker): starts_with_comment = True else: starts_with_comment = False + + line = _strip_code_flanked_in_backticks(line) + for word, word_start_at in self.tokenizer(line.strip()): word_start_at += initial_space lower_cased_word = word.casefold() diff --git a/tests/checkers/unittest_spelling.py b/tests/checkers/unittest_spelling.py index dec6b19ee..0d946bc84 100644 --- a/tests/checkers/unittest_spelling.py +++ b/tests/checkers/unittest_spelling.py @@ -37,7 +37,9 @@ if enchant is not None: pass -class TestSpellingChecker(CheckerTestCase): +class TestSpellingChecker( + CheckerTestCase +): # pylint:disable=too-many-public-methods # This is a test case class, not sure why it would be relevant to have this pylint rule enforced for test case classes CHECKER_CLASS = spelling.SpellingChecker skip_on_missing_package_or_dict = pytest.mark.skipif( @@ -303,37 +305,6 @@ class TestSpellingChecker(CheckerTestCase): ): self.checker.visit_classdef(stmt) - # @skip_on_missing_package_or_dict - # # @set_config(spelling_dict=spell_dict) - # @pytest.mark.parametrize( - # "num_files,num_jobs,num_checkers", - # [ - # (1, 2, 1), - # (1, 2, 2), - # (1, 2, 3), - # (2, 2, 1), - # (2, 2, 2), - # (2, 2, 3), - # (3, 2, 1), - # (3, 2, 2), - # (3, 2, 3), - # (3, 1, 1), - # (3, 1, 2), - # (3, 1, 3), - # (3, 5, 1), - # (3, 5, 2), - # (3, 5, 3), - # (10, 2, 1), - # (10, 2, 2), - # (10, 2, 3), - # (2, 10, 1), - # (2, 10, 2), - # (2, 10, 3), - # ], - # ) - # def test_compare_workers_to_single_proc(self, num_files, num_jobs, num_checkers): - # assert True - @skip_on_missing_package_or_dict @set_config(spelling_dict=spell_dict) @pytest.mark.parametrize( @@ -351,6 +322,7 @@ class TestSpellingChecker(CheckerTestCase): ("noqa", ":", "flake8 / zimports directive"), ("nosec", "", "bandit directive"), ("isort", ":skip", "isort directive"), + ("mypy", ":", "mypy directive"), ), ) def test_skip_tool_directives_at_beginning_of_comments_but_still_raise_error_if_directive_appears_later_in_comment( # pylint:disable=unused-argument # Having the extra description parameter allows the description to show up in the pytest output as part of the test name when running parametrized tests @@ -373,6 +345,42 @@ class TestSpellingChecker(CheckerTestCase): @skip_on_missing_package_or_dict @set_config(spelling_dict=spell_dict) + def test_skip_code_flanked_in_double_backticks(self): + full_comment = "# The function ``.qsize()`` .qsize()" + with self.assertAddsMessages( + Message( + "wrong-spelling-in-comment", + line=1, + args=( + "qsize", + full_comment, + " ^^^^^", + self._get_msg_suggestions("qsize"), + ), + ) + ): + self.checker.process_tokens(_tokenize_str(full_comment)) + + @skip_on_missing_package_or_dict + @set_config(spelling_dict=spell_dict) + def test_skip_code_flanked_in_single_backticks(self): + full_comment = "# The function `.qsize()` .qsize()" + with self.assertAddsMessages( + Message( + "wrong-spelling-in-comment", + line=1, + args=( + "qsize", + full_comment, + " ^^^^^", + self._get_msg_suggestions("qsize"), + ), + ) + ): + self.checker.process_tokens(_tokenize_str(full_comment)) + + @skip_on_missing_package_or_dict + @set_config(spelling_dict=spell_dict) def test_handle_words_joined_by_forward_slash(self): stmt = astroid.extract_node( ''' |