Skip to content

B031: don't count store-context references as generator uses#558

Merged
cooperlees merged 1 commit into
PyCQA:mainfrom
StressTestor:fix/b031-store-context-fp
Jun 16, 2026
Merged

B031: don't count store-context references as generator uses#558
cooperlees merged 1 commit into
PyCQA:mainfrom
StressTestor:fix/b031-store-context-fp

Conversation

@StressTestor

Copy link
Copy Markdown
Contributor

what

fixes a B031 false positive: type-annotating (or assigning to) the groupby loop variable was counted as a "use", so a single real use plus an annotation tripped "used more than once".

for _, group in groupby(items):
    group: SomeType        # annotation, not a use
    do_something(group)    # the only real use

before: B031 fires. after: clean.

why

the B031 usage-counter walked the loop body and counted every Name matching the group variable regardless of context. an annotation target (group: T) or an assignment target is a Store-context Name, not a use of the generator, so it inflated the count. count only Load-context references.

scope

this is the type-annotation / store-context case raised in the comments on #465. the original if/else case is handled separately by #557, and the two are orthogonal: if/else uses are both loads, untouched by this change, so they don't collide. real double-uses still fire B031.

tests

a case in tests/eval_files/b031.py that annotates the loop variable then uses it once, expecting no B031. it fails before the fix and passes after. full suite stays green.

Addresses #465.

A reference to the groupby loop variable in store context (an annotation
target like `group: T`, or an assignment target) was counted as a use of
the generator, so a single real use plus an annotation tripped a
false-positive B031. Count only loads.

Addresses PyCQA#465 (the annotation/store-context case; the if/else case is
handled separately by PyCQA#557).

@cooperlees cooperlees left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice - Thanks for the test showing it do as you claim too!

Let's just see if copilot nit picks anything useful before merge.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Fixes a B031 false positive by ensuring the “group” variable from itertools.groupby() is only counted as “used” when it’s read (AST Load context), not when it’s merely a store-context reference (e.g., annotation/assignment targets). This aligns B031 with its intent—detecting multiple consumptions of the group iterator—without inflating counts due to Store-context nodes.

Changes:

  • Update the B031 usage counter to count only ast.Name nodes in ast.Load context.
  • Add an eval-file regression case where the loop variable is annotated and then used once (no B031 expected).
  • Document the fix in the UNRELEASED changelog.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated no comments.

File Description
bugbear.py Restricts B031 “usage” counting (and nested-loop detection) to Load-context name references.
tests/eval_files/b031.py Adds a regression example ensuring loop-variable annotations don’t trigger B031.
README.rst Adds an UNRELEASED changelog entry describing the B031 behavior fix.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@cooperlees cooperlees merged commit e07f651 into PyCQA:main Jun 16, 2026
6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants