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

fix(flake8_boolean_trap): add whitelist for dict methods #943

Merged
merged 4 commits into from
Nov 28, 2022

Conversation

pwoolvett
Copy link
Contributor

@pwoolvett pwoolvett commented Nov 28, 2022

This commit enables the skipping of FBT003 based on two conditions:

  • function name must be explicitly whitelisted (for now, common dict methods). In the future, more could be added based on user requests.
  • argument in function call must be one of the first two. The idea is to skip only things like {}.get(False, "default") (for boolean keys) and {}.get(key, True) (for boolean defaults). Other method signature with a name in the whitelist can still potentially generate FBT003.

fixes #893

This commit enables the skipping of FBT003 based on two conditions:

* function name must be explicitly whitelisted (for now, common dict
  methods). In the future, more could be added based on user requests.
* argument in function call must be one of the first two. The idea is to
  skip only things like `{}.get(False, "default")` or
  `{}.get(key, True)`. Other method signature with a name in the
  whitelist can still potentially generate FBT003.

fixes astral-sh#893
@pwoolvett
Copy link
Contributor Author

#893
@obi-jerome,

@andersk
Copy link
Contributor

andersk commented Nov 28, 2022

Upstream doesn’t raise FBT003 for method calls at all:

https://github.com/pwoolvett/flake8_boolean_trap/blob/7783a47196411de1ca0d0670e9acf1be88829764/src/flake8_boolean_trap.py#L189

///
/// Conditions:
/// * function name must be explicitly whitelisted
/// * argument in function call must be one of the first two
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should it not be exactly the second argument, of two? Why "one of the first two"?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

booleans can be used as keys, too

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh right!

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess, as an exception, the first argument of fromkeys shouldn't be boolean given that it's always a tuple. That's fine though.

@@ -5,6 +5,8 @@ use crate::ast::types::Range;
use crate::check_ast::Checker;
use crate::checks::{Check, CheckKind};

const FUNC_NAME_WHITELIST: &[&str] = &["get", "setdefault", "pop", "fromkeys"];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider the more inclusive term “allowlist”.

If we’re going with this method name heuristic, some other candidates that come to mind are set.add, list.append, set.discard, list.count, list.index, list.insert, list.remove. There could be a lot of these, even before considering methods of custom classes. I think we should maybe start with upstream’s approach of ignoring method calls entirely?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, at least it's an independent check code so in theory can be opted out?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It’s not independent from the code used for non-method function calls.

$ cat test.py
def f(x):
    pass


f(True)
[].append(True)

$ flake8 test.py
test.py:5:1: FBT003 do not use boolean positional args. Hint: in `f(..)`, refactor positional arg #0 to include its argument name

$ ruff --select=FBT test.py
Found 2 error(s).
test.py:5:3: FBT003 Boolean positional value in function call
test.py:6:11: FBT003 Boolean positional value in function call

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh sorry, I misunderstood here. What do you think, @pwoolvett?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My gut tells me many people will have fbt003 disabled. For it to be useful, we should have an explicit allowlist. Its not a pain to maintain, and prs are easy to merge: add test case with false positive and name to allowlist. list will grow and after a couple of iterations it should be stable. Dev will have to pay the price of potential false negatives, but its still better than no fbt003.

  • Custom classes should be addressed by the dev when a bt is raised on codebase.
  • For cases such as when a library emits a bt, eventually an option in the toml could be used to extend the allow list. But do note the main issue here is the signature for those stdlib methods explicitly disallows named arguments with the / token. Other cases are expected to be improved by the dev when possible by using named args.
  • I have nothing against splitting func (fbt003)/methods (fbt004?), keeping comments above in mind.

note: whatever is decided here will be implemented upstream eventually to keep them in sync.

If you want to debate further, maybe discussions would be a better place, but i think theyre not ensbled...

@charliermarsh charliermarsh merged commit c4a7344 into astral-sh:main Nov 28, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

FBT003 triggered on dict
3 participants