From f066f828f3d1cffdc521033f59847926ba640dd2 Mon Sep 17 00:00:00 2001 From: Batuhan Taskaya Date: Tue, 5 Oct 2021 09:18:03 +0200 Subject: [PATCH 1/4] B904: ensure the raise is in the same context with the except --- bugbear.py | 40 +++++++++++++++++++++++++++++++++++++--- tests/b904.py | 7 +++++++ 2 files changed, 44 insertions(+), 3 deletions(-) diff --git a/bugbear.py b/bugbear.py index ba0294f..32a869c 100644 --- a/bugbear.py +++ b/bugbear.py @@ -16,6 +16,19 @@ __version__ = "21.9.2" LOG = logging.getLogger("flake8.bugbear") +CONTEXTFUL_NODES = ( + ast.Module, + ast.ClassDef, + ast.AsyncFunctionDef, + ast.FunctionDef, + ast.Lambda, + ast.ListComp, + ast.SetComp, + ast.DictComp, + ast.GeneratorExp, +) + +Context = namedtuple("Context", ["node", "stack"]) @attr.s(hash=False) @@ -147,10 +160,11 @@ def _typesafe_issubclass(cls, class_or_tuple): class BugBearVisitor(ast.NodeVisitor): filename = attr.ib() lines = attr.ib() - node_stack = attr.ib(default=attr.Factory(list)) + contexts = attr.ib(default=attr.Factory(list)) node_window = attr.ib(default=attr.Factory(list)) errors = attr.ib(default=attr.Factory(list)) futures = attr.ib(default=attr.Factory(set)) + context = attr.ib(default=attr.Factory(list)) NODE_WINDOW_SIZE = 4 @@ -161,13 +175,30 @@ def __getattr__(self, name): print(name) return self.__getattribute__(name) + @property + def node_stack(self): + if len(self.contexts) == 0: + return [] + + context, stack = self.contexts[-1] + return stack + def visit(self, node): + is_contextful = isinstance(node, CONTEXTFUL_NODES) + + if is_contextful: + context = Context(node, []) + self.contexts.append(context) + self.node_stack.append(node) self.node_window.append(node) self.node_window = self.node_window[-self.NODE_WINDOW_SIZE :] super().visit(node) self.node_stack.pop() + if is_contextful: + self.contexts.pop() + def visit_ExceptHandler(self, node): if node.type is None: self.errors.append( @@ -479,9 +510,12 @@ def check_for_b901(self, node): break def check_for_b902(self, node): - if not isinstance(self.node_stack[-2], ast.ClassDef): + if not len(self.contexts) >= 2 or not isinstance( + self.contexts[-2].node, ast.ClassDef + ): return + cls = self.contexts[-2].node decorators = NameFinder() decorators.visit(node.decorator_list) @@ -490,7 +524,7 @@ def check_for_b902(self, node): # `cls`? return - bases = {b.id for b in self.node_stack[-2].bases if isinstance(b, ast.Name)} + bases = {b.id for b in cls.bases if isinstance(b, ast.Name)} if "type" in bases: if ( "classmethod" in decorators.names diff --git a/tests/b904.py b/tests/b904.py index 104fda8..3967f00 100644 --- a/tests/b904.py +++ b/tests/b904.py @@ -19,3 +19,10 @@ raise base_err finally: raise Exception("Nothing to chain from, so no warning here") + +try: + raise ValueError +except ValueError: + # should not emit, since we are not raising something + def proxy(): + raise NameError From 168480f79c76ed5bba3d425dbe5ee71f8525d8b4 Mon Sep 17 00:00:00 2001 From: Batuhan Taskaya Date: Thu, 28 Oct 2021 19:52:32 +0200 Subject: [PATCH 2/4] not len() >= 2 to len() < 2 Co-authored-by: Cooper Lees --- bugbear.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bugbear.py b/bugbear.py index 32a869c..15bf0cd 100644 --- a/bugbear.py +++ b/bugbear.py @@ -510,7 +510,7 @@ def check_for_b901(self, node): break def check_for_b902(self, node): - if not len(self.contexts) >= 2 or not isinstance( + if len(self.contexts) < 2 or not isinstance( self.contexts[-2].node, ast.ClassDef ): return From e37c285faf1561f4d1795c569d4e28b95a76c07b Mon Sep 17 00:00:00 2001 From: Batuhan Taskaya Date: Thu, 28 Oct 2021 19:58:50 +0200 Subject: [PATCH 3/4] add tests about multi-level contexts --- tests/b904.py | 24 ++++++++++++++++++++++++ tests/test_bugbear.py | 1 + 2 files changed, 25 insertions(+) diff --git a/tests/b904.py b/tests/b904.py index 3967f00..44d1659 100644 --- a/tests/b904.py +++ b/tests/b904.py @@ -26,3 +26,27 @@ # should not emit, since we are not raising something def proxy(): raise NameError + +try: + from preferred_library import Thing +except ImportError: + try: + from fallback_library import Thing + except ImportError: + class Thing: + def __getattr__(self, name): + # same as the case above, should not emit. + raise AttributeError + + +try: + from preferred_library import Thing +except ImportError: + try: + from fallback_library import Thing + except ImportError: + def context_switch(): + try: + raise ValueError + except ValueError: + raise Exception diff --git a/tests/test_bugbear.py b/tests/test_bugbear.py index 445918d..c122d20 100644 --- a/tests/test_bugbear.py +++ b/tests/test_bugbear.py @@ -272,6 +272,7 @@ def test_b904(self): B904(10, 8), B904(11, 4), B904(16, 4), + B904(52, 16), ] self.assertEqual(errors, self.errors(*expected)) From f5da337688a5f00dc253514631a5623e81c8980f Mon Sep 17 00:00:00 2001 From: Cooper Lees Date: Sat, 27 Nov 2021 09:01:42 -0800 Subject: [PATCH 4/4] Update bugbear.py Change the self.context to self.contexts ... --- bugbear.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bugbear.py b/bugbear.py index 0bae75b..16815a2 100644 --- a/bugbear.py +++ b/bugbear.py @@ -181,7 +181,7 @@ class BugBearVisitor(ast.NodeVisitor): node_window = attr.ib(default=attr.Factory(list)) errors = attr.ib(default=attr.Factory(list)) futures = attr.ib(default=attr.Factory(set)) - context = attr.ib(default=attr.Factory(list)) + contexts = attr.ib(default=attr.Factory(list)) NODE_WINDOW_SIZE = 4