B031: allow reusing groups after materialization - #576
Conversation
Recognize list and tuple assignments back to the group name after checking their right-hand side. Preserve diagnostics on paths where the original generator can still be used, including conditional branches and potentially empty nested loops.
There was a problem hiding this comment.
🟢 Approval recommended
The implementation and new eval coverage align with the stated behavior change and appear to preserve conservative warnings in ambiguous control-flow cases.
Pull request overview
This PR updates the B031 check (groupby-group reuse) so that reassigning group = list(group) or group = tuple(group) back to the same variable is recognized as “materialized and now reusable,” avoiding false positives like #395 while still being conservative in conditionals and potentially-empty nested loops.
Changes:
- Teach B031’s AST walk to detect same-name
list(...)/tuple(...)materialization assignments (including annotated and chained assignments) and treat the name as reusable afterward. - Adjust B031 path-merging for nested loops and other constructs to avoid assuming assignments in potentially-optional/deferred execution definitely ran.
- Add eval coverage for direct/annotated/chained materialization, conditional branches, nested loops,
iter(), and deferred function bodies; document the change in the changelog.
File summaries
| File | Description |
|---|---|
bugbear.py |
Adds materialization detection for B031 and refines control-flow handling to avoid spurious repeated-use warnings after list(group) / tuple(group) reassignment. |
tests/eval_files/b031.py |
Adds regression/eval cases covering materialization and conservative merging scenarios (conditionals, nested loops, deferred bodies). |
README.rst |
Notes the B031 behavior change in the UNRELEASED changelog. |
Review details
- Files reviewed: 3/3 changed files
- Comments generated: 0
- Review effort level: Lite
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
cooperlees
left a comment
There was a problem hiding this comment.
Agree with copilot. Many thanks for adding this!
|
@copilot resolve the merge conflicts on this branch. |
After
group = list(group), B031 currently flags later reads ofgroupeven though it now holds a reusable list. This recognizes explicitlist(group)andtuple(group)assignments back to the same name, including annotated and chained assignments.The assignment's right-hand side is checked before the name becomes reusable. Conditional paths are merged conservatively, so a conversion on only one branch or inside a potentially empty nested loop does not suppress genuine repeated-use warnings. Arbitrary calls such as
iter(group)retain the existing behavior; this does not add alias analysis or infer the return types of helper functions.Fixes #395.
Validation:
tox -e py313,py314: Python 3.13 — 80 passed, 1 version-specific skip; Python 3.14 — 81 passed. Both report 98% coverage.pre-commit run --all-files: isort, black, flake8, and rstcheck passed.iter(), nested loops, and deferred function bodies.