-
Notifications
You must be signed in to change notification settings - Fork 1.1k
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
B023 is emitted when closure is defined inside a loop and uses a variable from the same loop #7847
Comments
Thanks for the well written issue :) this does look like a bug, even if I move return the function and run it in a new scope the variable is indeed bound def make_squared_list(size: int) -> list[int]:
result = []
for i in range(size):
square = i**2
def _append_to_result() -> None:
# Function definition does not bind loop variable `square` Ruff[B023](https://docs.astral.sh/ruff/rules/function-uses-loop-
# variable)
# (variable) square: int
print(square)
result.append(square)
_append_to_result()
return _append_to_result
make_squared_list(3)()
|
(Confirmed that flake8-bugbear also flags these cases. So it's not a bug in our re-implementation. We should still fix if we can.) |
Hmm, I looked at some bugbear issues, and it does seem like this is valid to flag in some cases. For example, this prints def make_squared_list(size: int) -> list[int]:
result = []
functions = []
for i in range(size):
square = i**2
def _append_to_result() -> None:
# Function definition does not bind loop variable `square` Ruff[B023](https://docs.astral.sh/ruff/rules/function-uses-loop-
# variable)
# (variable) square: int
result.append(square)
functions.append(_append_to_result)
for function in functions:
function()
return result
print(make_squared_list(2)) |
I think this false positive could be eliminated without risking any false negatives. We should be able to detect whether the only thing that is done with the function is calling it within the same loop. for i in range(size):
square = i**2
def _append_to_result() -> None:
result.append(square)
_append_to_result()
return result Nothing is done besides calling it in the same loop, so it should not be reported. for i in range(size):
square = i**2
def _append_to_result() -> None:
result.append(square)
_append_to_result()
return _append_to_result Something is done besides calling it in the same loop (returning it), so it should be reported. for i in range(size):
square = i**2
def _append_to_result() -> None:
result.append(square)
functions.append(_append_to_result) Something is done besides calling it in the same loop (passing it to another function), so it should be reported. In case someone wonders why someone would want to define a function only to call it within the same loop, the biggest reason I do it is code organization.
In one place I ran into this false positive, there were 5 variables captured by the function. |
I think @beauxq is right and ruff should be able to detect the first case where the only thing being done with the function is calling it within the same loop. |
Code snippet that reproduces the bug
The following snippet emits B023 (function-uses-loop-variable):
I have found out that adding a
nonlocal <variable>
declaration followed by an assignment to itself makes the error go away, i.e. change the closure_append_to_result
to this:Command invoked
Current ruff settings
Relevant sections from
pyproject.toml
:Current ruff version
The text was updated successfully, but these errors were encountered: