Adjustment: Do not trigger SIM113 if the counter is used outside the loop.
Explanation
A for loop only assigns the value variables if the loop iterates. If the iterable is empty, the loop never executes, and the variable is not assigned to. This works the same way even if you use enumerate(). Thus, if your iterable is empty, the counter variable will never be assigned to and your code will break.
Example
Suppose I'm given an iterable -- any iterable, could be a generator -- and I need to count the number of items I've processed in the loop, then do something with it. This always works, but (understandably) triggers SIM113:
n = 0
for x in stuff:
print(f"{n}: {x!r}")
n += 1
print(f"Processed {n} items")
However, if we change it to what the linter wants:
for i, x in enumerate(stuff, start=1):
print(f"{i} {x!r}")
print(f"Processed {i} items")
If stuff is empty, i will never be assigned to, and the last line will trigger a NameError. My code needs to accept any iterable, including unsized ones, so I can't do a length check beforehand.
Desired change
Explanation
A
for
loop only assigns the value variables if the loop iterates. If the iterable is empty, the loop never executes, and the variable is not assigned to. This works the same way even if you useenumerate()
. Thus, if your iterable is empty, the counter variable will never be assigned to and your code will break.Example
Suppose I'm given an iterable -- any iterable, could be a generator -- and I need to count the number of items I've processed in the loop, then do something with it. This always works, but (understandably) triggers SIM113:
However, if we change it to what the linter wants:
If
stuff
is empty,i
will never be assigned to, and the last line will trigger aNameError
. My code needs to accept any iterable, including unsized ones, so I can't do a length check beforehand.