diff --git a/README.rst b/README.rst index d8cfd18..c806a12 100644 --- a/README.rst +++ b/README.rst @@ -499,6 +499,7 @@ UNRELEASED ~~~~~~~~~~ * B018: handle also useless calls such as `isinstance(x, int)` without assigning or using the result +* B031: don't count a store-context reference (e.g. an annotation target like `group: T`) as a use of the `groupby` generator (#465) 25.11.29 ~~~~~~~~ diff --git a/bugbear.py b/bugbear.py index 329c435..c7d67cc 100644 --- a/bugbear.py +++ b/bugbear.py @@ -1215,11 +1215,18 @@ def check_for_b031(self, loop_node: ast.For) -> None: # noqa: C901 if ( isinstance(nested_node, ast.Name) and nested_node.id == group_name + and isinstance(nested_node.ctx, ast.Load) ): self.add_error("B031", nested_node, nested_node.id) - # Handle multiple uses - if isinstance(node, ast.Name) and node.id == group_name: + # Handle multiple uses. Count only loads: a store-context + # reference, such as an annotation target (`group: T`), is + # not a read of the generator (#465). + if ( + isinstance(node, ast.Name) + and node.id == group_name + and isinstance(node.ctx, ast.Load) + ): num_usages += 1 if num_usages > 1: self.add_error("B031", node, node.id) diff --git a/tests/eval_files/b031.py b/tests/eval_files/b031.py index 6580076..fd51a70 100644 --- a/tests/eval_files/b031.py +++ b/tests/eval_files/b031.py @@ -63,3 +63,9 @@ def collect_shop_items(shopper, items): ): collect_shop_items("Jane", group[1]) collect_shop_items("Joe", group[1]) + + +# Annotating the loop variable is not a second usage of the generator (#465) +for _section, section_items in groupby(items, key=lambda p: p[1]): + section_items: list + collect_shop_items("Jane", section_items)