From ab17c1f96b033c30faa7f0c17dc21764c9528560 Mon Sep 17 00:00:00 2001 From: Martin Imre Date: Mon, 22 Jan 2024 18:41:51 +0100 Subject: [PATCH] feat(b038): Change B038 to B909 and make it optional B038 lead to some false positives that stem from methods defined in the standard library that have the same name as mutating functions for container types like lists and dicts. Thus we decided to make this rule optional. See https://github.com/PyCQA/flake8-bugbear/issues/455 for the related discussion. --- bugbear.py | 32 +++++++++++++++++++++----------- tests/{b038.py => b909.py} | 0 tests/test_bugbear.py | 36 ++++++++++++++++++------------------ 3 files changed, 39 insertions(+), 29 deletions(-) rename tests/{b038.py => b909.py} (100%) diff --git a/bugbear.py b/bugbear.py index 918efba..f422df4 100644 --- a/bugbear.py +++ b/bugbear.py @@ -524,7 +524,7 @@ def visit_For(self, node): self.check_for_b020(node) self.check_for_b023(node) self.check_for_b031(node) - self.check_for_b038(node) + self.check_for_b909(node) self.generic_visit(node) def visit_AsyncFor(self, node): @@ -1574,17 +1574,17 @@ def check(num_args, param_name): elif node.func.attr == "split": check(2, "maxsplit") - def check_for_b038(self, node: ast.For): + def check_for_b909(self, node: ast.For): if isinstance(node.iter, ast.Name): name = _to_name_str(node.iter) elif isinstance(node.iter, ast.Attribute): name = _to_name_str(node.iter) else: return - checker = B038Checker(name) + checker = B909Checker(name) checker.visit(node.body) for mutation in checker.mutations: - self.errors.append(B038(mutation.lineno, mutation.col_offset)) + self.errors.append(B909(mutation.lineno, mutation.col_offset)) def compose_call_path(node): @@ -1597,7 +1597,7 @@ def compose_call_path(node): yield node.id -class B038Checker(ast.NodeVisitor): +class B909Checker(ast.NodeVisitor): # https://docs.python.org/3/library/stdtypes.html#mutable-sequence-types MUTATING_FUNCTIONS = ( "append", @@ -2146,12 +2146,22 @@ def visit_Lambda(self, node): " statement." ) ) - -B950 = Error(message="B950 line too long ({} > {} characters)") - -B038 = Error( +B909 = Error( message=( - "B038 editing a loop's mutable iterable often leads to unexpected results/bugs" + "B909 editing a loop's mutable iterable often leads to unexpected results/bugs" ) ) -disabled_by_default = ["B901", "B902", "B903", "B904", "B905", "B906", "B908", "B950"] +B950 = Error(message="B950 line too long ({} > {} characters)") + + +disabled_by_default = [ + "B901", + "B902", + "B903", + "B904", + "B905", + "B906", + "B908", + "B909", + "B950", +] diff --git a/tests/b038.py b/tests/b909.py similarity index 100% rename from tests/b038.py rename to tests/b909.py diff --git a/tests/test_bugbear.py b/tests/test_bugbear.py index 29bc7d0..8aed9fc 100644 --- a/tests/test_bugbear.py +++ b/tests/test_bugbear.py @@ -46,7 +46,6 @@ B035, B036, B037, - B038, B901, B902, B903, @@ -55,6 +54,7 @@ B906, B907, B908, + B909, B950, BugBearChecker, BugBearVisitor, @@ -969,27 +969,27 @@ def test_selfclean_test_bugbear(self): self.assertEqual(proc.stdout, b"") self.assertEqual(proc.stderr, b"") - def test_b038(self): - filename = Path(__file__).absolute().parent / "b038.py" - mock_options = Namespace(select=[], extend_select=["B038"]) + def test_b909(self): + filename = Path(__file__).absolute().parent / "b909.py" + mock_options = Namespace(select=[], extend_select=["B909"]) bbc = BugBearChecker(filename=str(filename), options=mock_options) errors = list(bbc.run()) print(errors) expected = [ - B038(11, 8), - B038(26, 8), - B038(27, 8), - B038(41, 8), - B038(47, 8), - B038(56, 8), - B038(57, 8), - B038(58, 8), - B038(59, 8), - B038(60, 8), - B038(61, 8), - B038(62, 8), - B038(63, 8), - B038(74, 8), + B909(11, 8), + B909(26, 8), + B909(27, 8), + B909(41, 8), + B909(47, 8), + B909(56, 8), + B909(57, 8), + B909(58, 8), + B909(59, 8), + B909(60, 8), + B909(61, 8), + B909(62, 8), + B909(63, 8), + B909(74, 8), ] self.assertEqual(errors, self.errors(*expected))