-
Notifications
You must be signed in to change notification settings - Fork 107
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 false alarms #269
Comments
Yep, this is one of those false alarms! I'd use a list-comprehension instead, which is both faster and IMO more readable: def filter_values(values: list[list[int]], max_percentage: float):
for value in values:
filter_val = max(value) * max_percentage
yield [x for x in values if x < filter_val] Unfortunately it's more difficult to avoid this false alarm than you might think: for example "lambdas are OK if they're the first argument in a call to |
Yeah I agree with you there, this was a silly example to illustrate the false postive. Real example this was triggered on is more convoluted (but less silly).
Oh yeah, I can see where this gets annoying to impliment. Wondering outloud though if we value reducing false positives or false negatives more? As in would the rule be more useful if it only flagged an error when it was very sure of the problem (the consequence being it lets some problems slide, and we get more false negatives). For the record, okay if we mark this as a known false positive! |
I'm down to add known false positives into the README to help save people time or maybe even allow for someone smarter to come up with a way to handle these edge cases. Maybe even state that in the README. |
I... don't think the real example is really a false alarm? (terminology note: I much prefer the "false alarm"/"missed alarm" to the "false positive/negative", because it makes it much easier to tell what you're talking about, especially when the alarm is for a negative outcome.) for token in iterator:
if (token_type := token.type) in open_close_pairs:
close_tag = open_close_pairs[token_type]
if token_type == close_tag:
yield iter([token])
else:
yield itertools.chain(
(token,),
itertools.takewhile(
lambda token_: token_.type != close_tag, iterator
),
) Unless you consume each yielded iterator before getting the next, you might have the wrong close tag! Safer to explicitly bind it in |
Not the same usecase as described here originally, but I found a false alarm with the following code: $ cat app.py
from app import db
for name in ['a', 'b']:
def myfunc(name):
qry = db.query(name=name)
db.run(qry)
myfunc(name=name)
qry = db.query(description__contains=name)
db.run(qry) And when I run flake8:
Basically - if I have a variable defined within the loop - but the same variable name is also within the function. But - the rule thinks it is an issue |
Just ran into one in Jinja pallets/jinja#1681 where if a file in a list of files doesn't exist, we continue the loop, otherwise we create a function that uses the file and return it, ending the loop. for name in names:
if not exists(name):
continue
def up_to_date():
if exists(name):
return mtime(name)
return False
return name, up_to_date
raise TemplateNotFound I refactored it to something along the lines of the following, which I'm fine with, but it did take a bit to figure out how to use for name in names:
if exists(name):
break
else:
raise TemplateNotFound
def up_to_date():
if exists(name):
return mtime(name)
return False
return name, up_to_date |
Could also do In principle we could avoid emitting the warning if we were sure that the it only occurs in the last loop iteration; in practice I'm not volunteering to write something that complicated at the moment. |
Another false positive: def foo(list_of_lists):
for l in list_of_lists:
priorities = calculate_priorities()
first = min(l, key=lambda x: priorities.index(x))
do_something(first) Unlike |
It's possible that |
This may be related to PyCQA/flake8-bugbear#269. These errors only occur if flake8 is run with Python 3.8, which is odd.
This may be related to PyCQA/flake8-bugbear#269. These errors only occur if flake8 is run with Python 3.8, which is odd.
just my 2c, this should maybe be off-by-default as it's difficult-to-impossible to know if the inner lambda / function is used in a deferred manner there's a bunch of examples similar to this when upgrading in (yes |
this example appears to trigger an unrelated false positive as well: def get_group_backfill_attributes(caches, group, events):
return {
k: v
for k, v in reduce(
lambda data, event: merge_mappings(
[data, {name: f(caches, data, event) for name, f in backfill_fields.items()}]
),
events,
{
name: getattr(group, name)
for name in set(initial_fields.keys()) | set(backfill_fields.keys())
},
).items()
if k in backfill_fields
} (yes the code is incomprehensible FP nonsense, but the |
def main():
numbers = list(range(5))
for number in numbers:
one_hundred_list = list(range(100))
squared = next(filter(lambda x: x == number**2, one_hundred_list))
print(squared) # This will print 0, 1, 4, 9, 16
if __name__ == "__main__":
main() Using |
Another false positive here: https://github.com/pimutils/vdirsyncer/blob/c50eabc77e3c30b1d20cd4fa9926459e4a081dc1/tests/storage/servers/davical/__init__.py#L42
|
My false-positive example: def foo(forbidden_calls, forbidden_functions, strings):
for fn in forbidden_calls:
f = forbidden_functions[fn]
waiver = any(map(lambda string: f['waiver_regex'].search(string), strings))
return waiver
|
I expect it'll be quite a while before I get back to this again, so here: main...Zac-HD:flake8-bugbear:B023-false-alarms This patch fixes the false alarm on functions where a local variable shadows the loop variable, and adds regression tests for the more common (and @cooperlees if you're making a release anyway, cherry-pick the partial fix?) |
I'm on it! |
The PR should fix a bunch of the false alarms mentioned in here, but probably not all of them. Feel free to check it out |
This issue is still present with the latest release and |
As written, you skip all but the last loop iteration, since for fn in forbidden_calls:
searcher = forbidden_functions[fn]['waiver_regex'].search
waiver = any(map(searcher, strings)) But more generally, yeah, we should handle |
Whoops, you are right, my code snippet is really suspicious. |
Done~ 🙂 |
This sample still fails. Shall II open a separate issue for it? for shipment in sender.shipments:
transaction.on_commit(lambda: update_single_me_shipment.delay(shipment.id) |
That's not a false alarm; unless the lambdas run immediately every single one will use the last shipment. |
Further comments to new issues please, we've fixed all the false alarms we know of. We've also received several reports where the linter is in fact correct, so consider refactoring your code to make it happy even if you think it's probably a false alarm! |
When upgrading, some false positives were found for B023 (implimented in #265).
Example
A silly example here, but one where the use of
filter_val
is valid.cc @Zac-HD
In the PR you mentioned some hard to detect false positives that you were okay with. Was this kind of pattern one of the ones you had in mind?
Thanks for this check btw, I think it's a good idea. I've fallen for this in the past!
The text was updated successfully, but these errors were encountered: