Repository navigation
B018: report map(), filter() and other lazy builtins used as statements - #605
Merged
Merged
Conversation
map(), filter(), reversed(), zip() and enumerate() return lazy iterators, so `map(print, items)` on its own line never calls `print`. They are now reported like the side-effect-free builtins B018 already covers. Like operations, a call directly in a `try` or `with` block is left alone, since these check their arguments right away and a test may call one only to see that raise. iter() is left out: it calls `__iter__` right away, which is what a bare `iter(x)` is for. The B905 and B912 tests call zip() and map() as statements, so their results are now assigned. Refs PyCQA#452
Contributor
There was a problem hiding this comment.
🟢 Approval recommended
The scoped detection change, exemptions, documentation, and fixture updates are consistent, with no unresolved findings.
No open issues 🎉
What changed in this PR
Extends B018’s useless-expression detection to report discarded calls to lazy builtins, addressing another case discussed in #452.
Changes:
- Detects standalone
map,filter,reversed,zip, andenumeratecalls while preserving directtry/withexemptions. - Adds coverage for reported calls and intentional exclusions, and adjusts existing fixtures to avoid unrelated B018 diagnostics.
- Documents the expanded behavior and adds a changelog entry.
| File | Description |
|---|---|
tests/eval_files/b912_py314.py |
Assigns map results and adjusts expected B912 columns. |
tests/eval_files/b905_py310.py |
Assigns zip results and adjusts expected B905 columns. |
tests/eval_files/b018_lazy_calls.py |
Covers discarded lazy calls, consumed results, and raising-block exemptions. |
README.rst |
Documents lazy-call detection and records the change. |
bugbear.py |
Adds lazy builtin names and integrates them into B018 detection. |
Review effort: Balanced
cooperlees
approved these changes
Oct 24, 2026
cooperlees
left a comment
Collaborator
There was a problem hiding this comment.
LGTM - Thanks for more work on reported issues!
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Refs #452.
B018 already reports a bare call to a builtin without side effects, like
sorted(x)orisinstance(x, int), but not the ones that return a lazy iterator, where the statement looks like it does something and doesn't:This adds
map,filter,reversed,zipandenumerate. The first three are the calls @JelleZijlstra listed in #452 next tosorted, which is already covered;zipandenumerateare lazy in the same way. It's also an easy leftover from Python 2, wheremap()andfilter()returned lists.iterstays out, since a bareiter(x)is there to callx.__iter__().Like the operations from #579, a call directly in a
try/withblock is left alone. These check their arguments right away, and tests call them only to see that raise, as CPython does withwith self.assertRaises(TypeError): zip(BadIterable())andtry: reversed(f) except TypeError:. As with the other builtins, the arguments aren't looked at: inmap(handle, fetch()),fetchruns buthandlenever does, so that's still reported.On CPython's
Libfrom main (2,071 files, tests included), Ubuntu's dist-packages (2,145) and about 14,000 files from other projects, this adds 3 reports:filter(identity, Squares(5))intest_builtin.py, which testsfilteritself, and twoenumerate(...)lines in a black formatter fixture. Both files already get B018.Tests are in
tests/eval_files/b018_lazy_calls.py.b905_py310.pyandb912_py314.pycallzip()andmap()as statements, so I assigned their results, like #547 did inb023.py. tox py310–py315 and pre-commit pass.