diff options
author | Pierre Sassoulas <pierre.sassoulas@gmail.com> | 2023-01-16 23:04:25 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-01-16 23:04:25 +0100 |
commit | 4a5b4e63c8177ec1b1c7d1ecb4eb2c448c7481a3 (patch) | |
tree | 33c7d491e2462d766aa0c3f360f9499e1cce809e | |
parent | eae0eaff11239db4dc89d570d120ff0a6cbe1e61 (diff) | |
download | pylint-git-4a5b4e63c8177ec1b1c7d1ecb4eb2c448c7481a3.tar.gz |
[style] Limit line length and complexity using flake8 (#8064)
125 is a good start. The check was activated in pylint with value = 100, but flake8 is less lenient than pylint and does not make any exceptions (for docstrings, strings and comments in particular).
-rw-r--r-- | doc/exts/pylint_messages.py | 4 | ||||
-rw-r--r-- | pylint/checkers/exceptions.py | 5 | ||||
-rw-r--r-- | pylint/checkers/similar.py | 49 | ||||
-rw-r--r-- | pylint/lint/pylinter.py | 3 | ||||
-rw-r--r-- | pylint/pyreverse/inspector.py | 7 | ||||
-rw-r--r-- | pylint/pyreverse/main.py | 9 | ||||
-rw-r--r-- | pylint/pyreverse/plantuml_printer.py | 3 | ||||
-rw-r--r-- | setup.cfg | 7 | ||||
-rw-r--r-- | tests/checkers/unittest_spelling.py | 53 | ||||
-rw-r--r-- | tests/test_func.py | 6 | ||||
-rw-r--r-- | tests/test_self.py | 12 |
11 files changed, 86 insertions, 72 deletions
diff --git a/doc/exts/pylint_messages.py b/doc/exts/pylint_messages.py index cef7c83a4..074e69387 100644 --- a/doc/exts/pylint_messages.py +++ b/doc/exts/pylint_messages.py @@ -290,8 +290,8 @@ def _generate_single_message_body(message: MessageData) -> str: if message.checker_module_name.startswith("pylint.extensions."): body += f""" .. note:: - This message is emitted by the optional :ref:`'{message.checker}'<{message.checker_module_name}>` checker which requires the ``{message.checker_module_name}`` - plugin to be loaded. + This message is emitted by the optional :ref:`'{message.checker}'<{message.checker_module_name}>` + checker which requires the ``{message.checker_module_name}`` plugin to be loaded. """ return body diff --git a/pylint/checkers/exceptions.py b/pylint/checkers/exceptions.py index fbe71a598..0e30e5386 100644 --- a/pylint/checkers/exceptions.py +++ b/pylint/checkers/exceptions.py @@ -544,7 +544,10 @@ class ExceptionsChecker(checkers.BaseChecker): def visit_compare(self, node: nodes.Compare) -> None: if isinstance(node.parent, nodes.ExceptHandler): # except (V < A) - suggestion = f"Did you mean '({node.left.as_string()}, {', '.join(operand.as_string() for _, operand in node.ops)})' instead?" + suggestion = ( + f"Did you mean '({node.left.as_string()}, " + f"{', '.join(o.as_string() for _, o in node.ops)})' instead?" + ) self.add_message("wrong-exception-operation", node=node, args=(suggestion,)) @utils.only_required_for_messages( diff --git a/pylint/checkers/similar.py b/pylint/checkers/similar.py index 3b18ddbfd..69b0677bd 100644 --- a/pylint/checkers/similar.py +++ b/pylint/checkers/similar.py @@ -5,16 +5,26 @@ """A similarities / code duplication command line tool and pylint checker. The algorithm is based on comparing the hash value of n successive lines of a file. -First the files are read and any line that doesn't fulfill requirement are removed (comments, docstrings...) +First the files are read and any line that doesn't fulfill requirement are removed +(comments, docstrings...) + Those stripped lines are stored in the LineSet class which gives access to them. -Then each index of the stripped lines collection is associated with the hash of n successive entries of the stripped lines starting at the current index -(n is the minimum common lines option). -The common hashes between both linesets are then looked for. If there are matches, then the match indices in both linesets are stored and associated -with the corresponding couples (start line number/end line number) in both files. -This association is then post-processed to handle the case of successive matches. For example if the minimum common lines setting is set to four, then -the hashes are computed with four lines. If one of match indices couple (12, 34) is the successor of another one (11, 33) then it means that there are -in fact five lines which are common. -Once post-processed the values of association table are the result looked for, i.e start and end lines numbers of common lines in both files. +Then each index of the stripped lines collection is associated with the hash of n +successive entries of the stripped lines starting at the current index (n is the +minimum common lines option). + +The common hashes between both linesets are then looked for. If there are matches, then +the match indices in both linesets are stored and associated with the corresponding +couples (start line number/end line number) in both files. + +This association is then post-processed to handle the case of successive matches. For +example if the minimum common lines setting is set to four, then the hashes are +computed with four lines. If one of match indices couple (12, 34) is the +successor of another one (11, 33) then it means that there are in fact five lines which +are common. + +Once post-processed the values of association table are the result looked for, i.e. +start and end lines numbers of common lines in both files. """ from __future__ import annotations @@ -457,7 +467,11 @@ class Similar: report += f" {line.rstrip()}\n" if line.rstrip() else "\n" duplicated_line_number += number * (len(couples_l) - 1) total_line_number: int = sum(len(lineset) for lineset in self.linesets) - report += f"TOTAL lines={total_line_number} duplicates={duplicated_line_number} percent={duplicated_line_number * 100.0 / total_line_number:.2f}\n" + report += ( + f"TOTAL lines={total_line_number} " + f"duplicates={duplicated_line_number} " + f"percent={duplicated_line_number * 100.0 / total_line_number:.2f}\n" + ) return report def _find_common( @@ -465,12 +479,15 @@ class Similar: ) -> Generator[Commonality, None, None]: """Find similarities in the two given linesets. - This the core of the algorithm. - The idea is to compute the hashes of a minimal number of successive lines of each lineset and then compare the hashes. - Every match of such comparison is stored in a dict that links the couple of starting indices in both linesets to - the couple of corresponding starting and ending lines in both files. - Last regroups all successive couples in a bigger one. It allows to take into account common chunk of lines that have more - than the minimal number of successive lines required. + This the core of the algorithm. The idea is to compute the hashes of a + minimal number of successive lines of each lineset and then compare the + hashes. Every match of such comparison is stored in a dict that links the + couple of starting indices in both linesets to the couple of corresponding + starting and ending lines in both files. + + Last regroups all successive couples in a bigger one. It allows to take into + account common chunk of lines that have more than the minimal number of + successive lines required. """ hash_to_index_1: HashToIndex_T hash_to_index_2: HashToIndex_T diff --git a/pylint/lint/pylinter.py b/pylint/lint/pylinter.py index e773644ff..4d1bede9a 100644 --- a/pylint/lint/pylinter.py +++ b/pylint/lint/pylinter.py @@ -1252,8 +1252,9 @@ class PyLinter( msg_cat = MSG_TYPES[message_definition.msgid[0]] self.msg_status |= MSG_TYPES_STATUS[message_definition.msgid[0]] self.stats.increase_single_message_count(msg_cat, 1) + # TODO: 3.0 Should be removable after https://github.com/PyCQA/pylint/pull/5580 self.stats.increase_single_module_message_count( - self.current_name, # type: ignore[arg-type] # Should be removable after https://github.com/PyCQA/pylint/pull/5580 + self.current_name, # type: ignore[arg-type] msg_cat, 1, ) diff --git a/pylint/pyreverse/inspector.py b/pylint/pyreverse/inspector.py index af97cdb33..9150bbef3 100644 --- a/pylint/pyreverse/inspector.py +++ b/pylint/pyreverse/inspector.py @@ -202,9 +202,10 @@ class Linker(IdGeneratorMixIn, utils.LocalsVisitor): if node.implements: # TODO: 3.0: Remove support for __implements__ warnings.warn( - "pyreverse will drop support for resolving and displaying implemented interfaces in pylint 3.0. " - "The implementation relies on the '__implements__' attribute proposed in PEP 245, which was rejected " - "in 2006.", + "pyreverse will drop support for resolving and displaying " + "implemented interfaces in pylint 3.0. The implementation " + "relies on the '__implements__' attribute proposed in PEP 245" + ", which was rejected in 2006.", DeprecationWarning, ) else: diff --git a/pylint/pyreverse/main.py b/pylint/pyreverse/main.py index 72429b41a..8a2055c41 100644 --- a/pylint/pyreverse/main.py +++ b/pylint/pyreverse/main.py @@ -145,8 +145,10 @@ OPTIONS: Options = ( "metavar": "<format>", "type": "string", "help": ( - f"create a *.<format> output file if format is available. Available formats are: {', '.join(DIRECTLY_SUPPORTED_FORMATS)}. " - f"Any other format will be tried to create by means of the 'dot' command line tool, which requires a graphviz installation." + "create a *.<format> output file if format is available. Available " + f"formats are: {', '.join(DIRECTLY_SUPPORTED_FORMATS)}. Any other " + f"format will be tried to create by means of the 'dot' command line " + f"tool, which requires a graphviz installation." ), }, ), @@ -221,7 +223,8 @@ class Run(_ArgumentsManager, _ArgumentsProvider): if self.config.output_format not in DIRECTLY_SUPPORTED_FORMATS: check_graphviz_availability() print( - f"Format {self.config.output_format} is not supported natively. Pyreverse will try to generate it using Graphviz..." + f"Format {self.config.output_format} is not supported natively." + " Pyreverse will try to generate it using Graphviz..." ) check_if_graphviz_supports_format(self.config.output_format) diff --git a/pylint/pyreverse/plantuml_printer.py b/pylint/pyreverse/plantuml_printer.py index 56463165d..de3f983b7 100644 --- a/pylint/pyreverse/plantuml_printer.py +++ b/pylint/pyreverse/plantuml_printer.py @@ -40,7 +40,8 @@ class PlantUmlPrinter(Printer): self.emit("top to bottom direction") else: raise ValueError( - f"Unsupported layout {self.layout}. PlantUmlPrinter only supports left to right and top to bottom layout." + f"Unsupported layout {self.layout}. PlantUmlPrinter only " + "supports left to right and top to bottom layout." ) def emit_node( @@ -12,15 +12,14 @@ license_files = # Incompatible with black see https://github.com/ambv/black/issues/315 # E203: Whitespace before ':' # W503: Line break occurred before a binary operator -# E501: Line too long # B028: consider using the `!r` conversion flag ignore = E203, W503, - E501, B028, -max-line-length=88 -max-complexity=39 +# Flake8 is less lenient than pylint and does not make any exceptions +# (for docstrings, strings and comments in particular). +max-line-length=125 # Required for flake8-typing-imports (v1.12.0) # The plugin doesn't yet read the value from pyproject.toml min_python_version = 3.7.2 diff --git a/tests/checkers/unittest_spelling.py b/tests/checkers/unittest_spelling.py index abeb9dcf8..29783429b 100644 --- a/tests/checkers/unittest_spelling.py +++ b/tests/checkers/unittest_spelling.py @@ -286,44 +286,29 @@ class TestSpellingChecker(CheckerTestCase): # pylint:disable=too-many-public-me @skip_on_missing_package_or_dict @set_config(spelling_dict=spell_dict) @pytest.mark.parametrize( - ",".join( - ( - "misspelled_portion_of_directive", - "second_portion_of_directive", - "description", - ) - ), + "prefix,suffix", ( - ("fmt", ": on", "black directive to turn on formatting"), - ("fmt", ": off", "black directive to turn off formatting"), - ("noqa", "", "pycharm directive"), - ("noqa", ":", "flake8 / zimports directive"), - ("nosec", "", "bandit directive"), - ("isort", ":skip", "isort directive"), - ("mypy", ":", "mypy top of file directive"), + pytest.param("fmt", ": on", id="black directive to turn on formatting"), + pytest.param("fmt", ": off", id="black directive to turn off formatting"), + pytest.param("noqa", "", id="pycharm directive"), + pytest.param("noqa", ":", id="flake8 / zimports directive"), + pytest.param("nosec", "", id="bandit directive"), + pytest.param("isort", ":skip", id="isort directive"), + pytest.param("mypy", ":", id="mypy top of file 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 parameterized tests. - self, - misspelled_portion_of_directive: str, - second_portion_of_directive: str, - description: str, - ) -> None: - full_comment = f"# {misspelled_portion_of_directive}{second_portion_of_directive} {misspelled_portion_of_directive}" + def test_tool_directives_handling(self, prefix: str, suffix: str) -> None: + """We're not raising when the directive is at the beginning of comments, + but we raise if a directive appears later in comment.""" + full_comment = f"# {prefix}{suffix} {prefix}" + args = ( + prefix, + full_comment, + f" {'^' * len(prefix)}", + self._get_msg_suggestions(prefix), + ) with self.assertAddsMessages( - MessageTest( - "wrong-spelling-in-comment", - line=1, - args=( - misspelled_portion_of_directive, - full_comment, - f" {'^'*len(misspelled_portion_of_directive)}", - self._get_msg_suggestions(misspelled_portion_of_directive), - ), - ) + MessageTest("wrong-spelling-in-comment", line=1, args=args) ): self.checker.process_tokens(_tokenize_str(full_comment)) diff --git a/tests/test_func.py b/tests/test_func.py index 493489aee..a032baf5d 100644 --- a/tests/test_func.py +++ b/tests/test_func.py @@ -73,9 +73,11 @@ class LintTestUsingModule: self.linter.check(tocheck) except Exception as ex: print(f"Exception: {ex} in {tocheck}:: {'‚ '.join(ex.args)}") - ex.file = tocheck # type: ignore[attr-defined] # This is legacy code we're trying to remove, not worth it to type correctly + # This is legacy code we're trying to remove, not worth it to type correctly + ex.file = tocheck # type: ignore[attr-defined] print(ex) - ex.__str__ = exception_str # type: ignore[assignment] # This is legacy code we're trying to remove, impossible to type correctly + # This is legacy code we're trying to remove, not worth it to type correctly + ex.__str__ = exception_str # type: ignore[assignment] raise assert isinstance(self.linter.reporter, GenericTestReporter) self._check_result(self.linter.reporter.finalize()) diff --git a/tests/test_self.py b/tests/test_self.py index 9cec1b1c9..076f2a22f 100644 --- a/tests/test_self.py +++ b/tests/test_self.py @@ -1052,19 +1052,21 @@ a.py:1:4: E0001: Parsing failed: 'invalid syntax (<unknown>, line 1)' (syntax-er [ ( "text", - "tests/regrtest_data/unused_variable.py:4:4: W0612: Unused variable 'variable' (unused-variable)", + "{path}:4:4: W0612: Unused variable 'variable' (unused-variable)", ), ( "parseable", - "tests/regrtest_data/unused_variable.py:4: [W0612(unused-variable), test] Unused variable 'variable'", + "{path}:4: [W0612(unused-variable), test] Unused variable 'variable'", ), ( "msvs", - "tests/regrtest_data/unused_variable.py(4): [W0612(unused-variable)test] Unused variable 'variable'", + "{path}(4): [W0612(unused-variable)test] Unused variable 'variable'", ), ( "colorized", - "tests/regrtest_data/unused_variable.py:4:4: W0612: \x1B[35mUnused variable 'variable'\x1B[0m (\x1B[35munused-variable\x1B[0m)", + ( + "{path}:4:4: W0612: \x1B[35mUnused variable 'variable'\x1B[0m (\x1B[35munused-variable\x1B[0m)" + ), ), ("json", '"message": "Unused variable \'variable\'",'), ], @@ -1077,7 +1079,7 @@ a.py:1:4: E0001: Parsing failed: 'invalid syntax (<unknown>, line 1)' (syntax-er self._test_output_file( [path, f"--output={output_file}", f"--output-format={output_format}"], output_file, - expected_output, + expected_output.format(path="tests/regrtest_data/unused_variable.py"), ) def test_output_file_can_be_combined_with_custom_reporter( |