diff options
author | Nick Drozd <nicholasdrozd@gmail.com> | 2021-08-18 10:28:49 -0500 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-08-18 17:28:49 +0200 |
commit | 53da6541dbe621b55f2c7f1bfcf821541dc823e8 (patch) | |
tree | 3f6f98aa51b609a177f975992baab14ef6839cd8 | |
parent | f78a35afb76c38f4141714130bbd1a35ee430ea1 (diff) | |
download | pylint-git-53da6541dbe621b55f2c7f1bfcf821541dc823e8.tar.gz |
Add extension check against use of while loops (#4860)
* Move stray functional tests
* Clean up some while loops
* Add extension check against use of while loops
-rw-r--r-- | ChangeLog | 4 | ||||
-rw-r--r-- | doc/whatsnew/2.10.rst | 4 | ||||
-rw-r--r-- | pylint/checkers/utils.py | 6 | ||||
-rw-r--r-- | pylint/config/option_manager_mixin.py | 5 | ||||
-rw-r--r-- | pylint/extensions/typing.py | 6 | ||||
-rw-r--r-- | pylint/extensions/while_used.py | 30 | ||||
-rw-r--r-- | pylint/lint/utils.py | 6 | ||||
-rw-r--r-- | pylint/reporters/ureports/__init__.py | 3 | ||||
-rw-r--r-- | pylint/utils/utils.py | 2 | ||||
-rw-r--r-- | tests/extensions/test_while_used.py | 25 | ||||
-rw-r--r-- | tests/functional/ext/while_used.py | 10 | ||||
-rw-r--r-- | tests/functional/ext/while_used.rc | 2 | ||||
-rw-r--r-- | tests/functional/ext/while_used.txt | 2 | ||||
-rw-r--r-- | tests/functional/i/import_outside_toplevel.py (renamed from tests/functional/import_outside_toplevel.py) | 0 | ||||
-rw-r--r-- | tests/functional/i/import_outside_toplevel.txt (renamed from tests/functional/import_outside_toplevel.txt) | 0 |
15 files changed, 88 insertions, 17 deletions
@@ -69,6 +69,10 @@ Release date: TBA Closes #4365 +* Added optional extension ``while_used``: Emitted whenever a ``while`` loop is used. + + Closes # 4367 + * Added ``forgotten-debug-statement``: Emitted when ``breakpoint``, ``pdb.set_trace`` or ``sys.breakpointhook`` calls are found Closes #3692 diff --git a/doc/whatsnew/2.10.rst b/doc/whatsnew/2.10.rst index e8a354a05..501c9156a 100644 --- a/doc/whatsnew/2.10.rst +++ b/doc/whatsnew/2.10.rst @@ -24,6 +24,10 @@ New checkers Closes #4365 +* Added optional extension ``while_used``: Emitted whenever a ``while`` loop is used. + + Closes # 4367 + * Added ``forgotten-debug-statement``: Emitted when ``breakpoint``, ``pdb.set_trace`` or ``sys.breakpointhook`` calls are found Closes #3692 diff --git a/pylint/checkers/utils.py b/pylint/checkers/utils.py index 47de79d25..aa445a708 100644 --- a/pylint/checkers/utils.py +++ b/pylint/checkers/utils.py @@ -671,9 +671,9 @@ def node_frame_class(node: astroid.node_classes.NodeNG) -> Optional[astroid.Clas and not isinstance(klass, astroid.ClassDef) ): if klass.parent is None: - klass = None - else: - klass = klass.parent.frame() + return None + + klass = klass.parent.frame() return klass diff --git a/pylint/config/option_manager_mixin.py b/pylint/config/option_manager_mixin.py index 02c6997af..84a2158ad 100644 --- a/pylint/config/option_manager_mixin.py +++ b/pylint/config/option_manager_mixin.py @@ -240,8 +240,7 @@ class OptionsManagerMixIn: """Read the configuration file but do not load it (i.e. dispatching values to each options provider) """ - help_level = 1 - while help_level <= self._maxlevel: + for help_level in range(1, self._maxlevel + 1): opt = "-".join(["long"] * help_level) + "-help" if opt in self._all_options: break # already processed @@ -255,7 +254,7 @@ class OptionsManagerMixIn: provider = self.options_providers[0] self.add_optik_option(provider, self.cmdline_parser, opt, optdict) provider.options += ((opt, optdict),) - help_level += 1 + if config_file is None: config_file = self.config_file if config_file is not None: diff --git a/pylint/extensions/typing.py b/pylint/extensions/typing.py index c2ba28de4..c931ea020 100644 --- a/pylint/extensions/typing.py +++ b/pylint/extensions/typing.py @@ -293,11 +293,7 @@ class TypingChecker(BaseChecker): """ if self._py37_plus() and not self._py39_plus(): msg_future_import = self._msg_postponed_eval_hint(node) - while True: - try: - msg = self._consider_using_alias_msgs.pop(0) - except IndexError: - break + for msg in self._consider_using_alias_msgs: if msg.qname in self._alias_name_collisions: continue self.add_message( diff --git a/pylint/extensions/while_used.py b/pylint/extensions/while_used.py new file mode 100644 index 000000000..9476478c3 --- /dev/null +++ b/pylint/extensions/while_used.py @@ -0,0 +1,30 @@ +"""Check for use of while loops.""" +from pylint.checkers import BaseChecker +from pylint.checkers.utils import check_messages +from pylint.interfaces import IAstroidChecker + + +class WhileChecker(BaseChecker): + + __implements__ = (IAstroidChecker,) + name = "while_used" + msgs = { + "W0149": ( + "Used `while` loop", + "while-used", + "Unbounded `while` loops can often be rewritten as bounded `for` loops.", + ) + } + + @check_messages("while-used") + def visit_while(self, node): + self.add_message("while-used", node=node) + + +def register(linter): + """Required method to auto register this checker. + + :param linter: Main interface object for Pylint plugins + :type linter: Pylint object + """ + linter.register_checker(WhileChecker(linter)) diff --git a/pylint/lint/utils.py b/pylint/lint/utils.py index 6c5390cb7..de24de988 100644 --- a/pylint/lint/utils.py +++ b/pylint/lint/utils.py @@ -78,7 +78,9 @@ def preprocess_options(args, search_for): i = 0 while i < len(args): arg = args[i] - if arg.startswith("--"): + if not arg.startswith("--"): + i += 1 + else: try: option, val = arg[2:].split("=", 1) except ValueError: @@ -99,8 +101,6 @@ def preprocess_options(args, search_for): msg = "Option %s doesn't expects a value" % option raise ArgumentPreprocessingError(msg) cb(option, val) - else: - i += 1 def _patch_sys_path(args): diff --git a/pylint/reporters/ureports/__init__.py b/pylint/reporters/ureports/__init__.py index 9b47bf378..2b0a52c50 100644 --- a/pylint/reporters/ureports/__init__.py +++ b/pylint/reporters/ureports/__init__.py @@ -75,8 +75,7 @@ class BaseWriter: cols -= 1 result[-1].append(cell) # fill missing cells - while len(result[-1]) < cols: - result[-1].append("") + result[-1] += [""] * (cols - len(result[-1])) return result def compute_content(self, layout): diff --git a/pylint/utils/utils.py b/pylint/utils/utils.py index b6c81284b..1a8f7957d 100644 --- a/pylint/utils/utils.py +++ b/pylint/utils/utils.py @@ -63,7 +63,7 @@ def get_module_and_frameid(node): try: frame = frame.parent.frame() except AttributeError: - frame = None + break obj.reverse() return module, ".".join(obj) diff --git a/tests/extensions/test_while_used.py b/tests/extensions/test_while_used.py new file mode 100644 index 000000000..23fe95f07 --- /dev/null +++ b/tests/extensions/test_while_used.py @@ -0,0 +1,25 @@ +"""Tests for the pylint checker in :mod:`pylint.extensions.while +""" + +import astroid + +from pylint.extensions.while_used import WhileChecker +from pylint.testutils import CheckerTestCase, Message + + +class TestWhileUsed(CheckerTestCase): + + CHECKER_CLASS = WhileChecker + + def test_while_used(self): + node = astroid.extract_node( + """ + def f(): + i = 0 + while i < 10: + i += 1 + """ + ).body[1] + + with self.assertAddsMessages(Message("while-used", node=node)): + self.checker.visit_while(node) diff --git a/tests/functional/ext/while_used.py b/tests/functional/ext/while_used.py new file mode 100644 index 000000000..848da0d51 --- /dev/null +++ b/tests/functional/ext/while_used.py @@ -0,0 +1,10 @@ +"""Functional test""" + +while True: # [while-used] + print("asdf") + +def fff(): + "zxcv" + i = 0 + while i < 10: # [while-used] + i += 1 diff --git a/tests/functional/ext/while_used.rc b/tests/functional/ext/while_used.rc new file mode 100644 index 000000000..28e1b1faf --- /dev/null +++ b/tests/functional/ext/while_used.rc @@ -0,0 +1,2 @@ +[MASTER] +load-plugins=pylint.extensions.while_used, diff --git a/tests/functional/ext/while_used.txt b/tests/functional/ext/while_used.txt new file mode 100644 index 000000000..80ca84d56 --- /dev/null +++ b/tests/functional/ext/while_used.txt @@ -0,0 +1,2 @@ +while-used:3:0::Used `while` loop +while-used:9:4:fff:Used `while` loop diff --git a/tests/functional/import_outside_toplevel.py b/tests/functional/i/import_outside_toplevel.py index 3721db613..3721db613 100644 --- a/tests/functional/import_outside_toplevel.py +++ b/tests/functional/i/import_outside_toplevel.py diff --git a/tests/functional/import_outside_toplevel.txt b/tests/functional/i/import_outside_toplevel.txt index d4b1786ea..d4b1786ea 100644 --- a/tests/functional/import_outside_toplevel.txt +++ b/tests/functional/i/import_outside_toplevel.txt |