From 82357dfc7fac2b70b3ff72caf6022b0b049e5a89 Mon Sep 17 00:00:00 2001 From: Martin Imre Date: Wed, 7 Feb 2024 05:11:17 +0100 Subject: [PATCH] feat(b038): Change B038 to B909 and make it optional (#456) 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 485a8e9..f9605a5 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))