PyCQA / flake8-bugbear

A plugin for Flake8 finding likely bugs and design problems in your program. Contains warnings that don't belong in pyflakes and pycodestyle.
MIT License
1.05k stars 103 forks source link

False positive `B023` with loop's local variable #402

Open jamesbraza opened 1 year ago

jamesbraza commented 1 year ago
def some_fn(*_) -> None:
    ...

for _ in range(1):
    foo = 0
    some_fn(lambda: foo)

Since foo is a local variable in the loop, B023 shouldn't happen here (unless I am mistaken about something).

Running flake8==6.0.0 with flake8-bugbear==23.7.10 on this:

> flake8 a.py
a.py:7:21: B023 Function definition does not bind loop variable 'foo'.

I think this relates to https://github.com/PyCQA/flake8-bugbear/issues/269 or https://github.com/PyCQA/flake8-bugbear/issues/380.

JelleZijlstra commented 12 months ago

In your example it doesn't matter because foo is always assigned to the same value, but in general this isn't a false positive. For example, if you instead had foo = foo + 1, then every loop iteration would have a different value, and the lambda might close over the value and see an updated one.

jamesbraza commented 12 months ago

Thanks for the response, just trying to understand it more. The below code with foo = foo + 1 triggers B023 as well, though I believe it also doesn't have binding risk:

def some_fn(fn) -> None:
    print(fn())

foo = 0
for _ in range(10):
    foo = foo + 1  # foo is a local variable in the loop iteration
    some_fn(lambda: foo)

Would you mind clarifying your example? I am trying to understand the line between false positive and true positive here.

Also, if you think this issue should be closed out, feel free to close it

tomasr8 commented 11 months ago

If the function invocation is delayed (for whatever reason) you'd get an unexpected behaviour. For example:

foo = 0
fns = []
for _ in range(10):
    foo = foo + 1
    fns.append(lambda: print(foo))

for fn in fns:
    fn()  # all print 10

I suppose it would be pretty hard to verify if the lambda is only invoked within the same iteration