Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rule B909 (loop-iterator-mutation) doesn't detect many kinds of list mutation #12164

Closed
Boon-in-Oz opened this issue Jul 3, 2024 · 3 comments · Fixed by #12365 or #12366
Closed

Rule B909 (loop-iterator-mutation) doesn't detect many kinds of list mutation #12164

Boon-in-Oz opened this issue Jul 3, 2024 · 3 comments · Fixed by #12365 or #12366
Assignees

Comments

@Boon-in-Oz
Copy link

Hello and thank you for this excellent tool,

I'm running Ruff as a linter in VS Code with select = ["ALL"], preview = true and only specific rules ignored. I'm still on Python 3.7.7, and using the Ruff 2024.28.0 extension.

I've found that B909 sometimes fires when I extend a list in a loop, but not when I remove or del an item from it. I'm not sure how the original Bugbear behaves in this case but in practice these seem very similar and any of them can cause bugs.

I can't find any report of this in issues, searching for "B909", "mutation", "iterator". The only issue I found was the original task to implement B038: #9511

Repros:

This does not generate any linting errors:
(from https://stackoverflow.com/questions/6260089/strange-result-when-removing-item-from-a-list-while-iterating-over-it-in-python)

numbers = list(range(1, 30))
for i in numbers:
    if i < 20:
        numbers.remove(i)
print(numbers)

[2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29]

But this triggers both B909 and PERF401:

numbers = list(range(1, 30))
for i in numbers:
    if i < 20:
        numbers.append(i + 20)
print(numbers)

This does not trigger B909 or PERF401 either (though it does correctly trigger FURB148 on the enumerate call)

numbers = list(range(1, 30))
for i, _ in enumerate(numbers):
    if i < 20:
        numbers.append(i)

Same for this:

numbers = list(range(1, 30))
for i, _ in enumerate(numbers):
    if i < 20:
        del numbers[i]
print(numbers)

[2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28]

And this (don't try this one, it's an infinite loop)

numbers = list(range(1, 30))
for i, _ in enumerate(numbers):
    if i > 20:
        numbers.append(i)
@charliermarsh
Copy link
Member

charliermarsh commented Jul 17, 2024

numbers = list(range(1, 30))
for i in numbers:
    if i < 20:
        numbers.remove(i)
print(numbers)

It looks like we specifically allow this pattern... For example, we do flag an error for this:

numbers = list(range(1, 30))
for i in numbers:
    if i < 20:
        numbers.remove(i + 1)
print(numbers)

The enumerate(...) variants we should probably catch.

@charliermarsh
Copy link
Member

I feel like we should still be flagging:

numbers = list(range(1, 30))
for i in numbers:
    if i < 20:
        numbers.remove(i)
print(numbers)

But need to look at the source to understand why that exception was added.

@charliermarsh
Copy link
Member

Ok, I think I should remove that exception -- it seems misguided.

@charliermarsh charliermarsh self-assigned this Jul 17, 2024
charliermarsh added a commit that referenced this issue Jul 17, 2024
charliermarsh added a commit that referenced this issue Jul 17, 2024
…tation` (#12365)

## Summary

Pretty sure this should still be an error, but also, I think I added
this because of ecosystem CI? So want to see what pops up.

Closes #12164.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
2 participants