-
-
Notifications
You must be signed in to change notification settings - Fork 1.2k
Add checker for nested min/max #7550
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
Add checker for nested min/max #7550
Conversation
Pull Request Test Coverage Report for Build 3510087842
💛 - Coveralls |
79d4ebd
to
5dea2aa
Compare
pylint/extensions/nested_min_max.py
Outdated
@@ -0,0 +1,54 @@ | |||
# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this can become part of stdlib.py
. I don't think it needs to be an extension.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does it mean it will be activated by default?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it would be better if we keep the file as it is before adding to stdlib (I think the base checker is an example of a checker dispathed across multiple files, the checker name is the same in all files).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Doesn't the splitting across multiple files make pylint
slower? There are more functional calls, class initialisations etc. If the file isn't too large we can combine them right?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It make everything harder to maintain though. Clearly differentiating checks help tremendously when keeping clean code because it's possible to understand everything about a 50 lines checker, but it's clearly impossible to understand and thus cleanup a 2000 lines stdlib checker behemoth if a function, constant (or more interestingly for performances: condition or calculation) become useless.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm, okay. So how would you want to add this then?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we want to make this a default check we can add a new checker (just move the file in pylint/checker) and make it a default checker (register it) with a new name or with an existing checker name.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Changed it into a new checker!
Let me know which existing checker name I should use if you have any preference
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would make the message clearer by stating the actual replacing code. (This could be useful if we want to automatically fix in the future too).
404d124
to
e3060e2
Compare
@osherdp Please don't force-push your branch. It makes reviewing more difficult. We will squash merge your changes into the |
I think the doc CI is failing because of missing good/bad examples for this new checker. Existing docs live here: https://github.com/PyCQA/pylint/tree/main/doc/data/messages |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for moving the checker. Could you also add a good.py
and bad.py
example?
pylint/checkers/nested_min_max.py
Outdated
) | ||
|
||
@classmethod | ||
def get_redundant_calls(cls, node: nodes.Call) -> nodes.NodeNG: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
def get_redundant_calls(cls, node: nodes.Call) -> nodes.NodeNG: | |
def get_redundant_calls(cls, node: nodes.Call) -> list[nodes.Call]: |
pylint/checkers/nested_min_max.py
Outdated
while len(redundant_calls) > 0: | ||
for i, arg in enumerate(node.args): | ||
if arg in redundant_calls: | ||
node.args = node.args[:i] + arg.args + node.args[i + 1 :] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's not reassign anything on node
itself. That changes the ast
representation and is a side effect that might not work well with future checkers.
Hi @osherdp are you still interested in working on this PR? |
Unfortunately I cannot find time to invest in it 😞 |
Merged upstream/main to get the primer's result. |
I think tests should now pass. min(min(min(1, 2), 3), 4) # [nested-min-max, nested-min-max] Raises twice which could be avoided by doing some name and inference look up on the parent of a |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM ! I'd suggest doing some documentation about the design choices. But feel free to disregard.
I forgot to wait for the primer's results. |
This adds a new checker (not active by default) which identifies usages similar to ``min(<arg1>, min(<arg2>, <arg3>))`` and suggests using a simplified form of ``min(<arg1>, <arg2>, <arg3>)``. Same goes for ``max`` usage. The logic is as follows: it detects calls to either ``min`` or ``max`` functions, and whenever one of their arguments is that same function, it emits the message.
6abf7b0
to
f035c32
Compare
Took over the MR so approve automatically.
This comment has been minimized.
This comment has been minimized.
I'm wondering how to make |
So, that would necessitate stopping the warning when there is a |
Maybe, but I'm not even sure it's a false positive tbh. |
I think the only code that works is: That doesn't seem very nice to read, but that might just be personal preference. |
I have the intuition that this is better performance-wise as there is only one max call. If that's the case it would not be a false positive. But the solution is not super obvious I had to think about for longer than what pylint user are ready to invest imo. |
So, fix? Or keep it as a feature? 😄 |
I was counting on you @jacobtylerwalls @mbyrnepr2 or @osherdp to take the decision for me 😄 Let's vote ! This is a false positive, we need to fix: : 👀 |
The proposed fix uses one less |
I voted in favour of fixing the false positive; the broken code (it is broken, right?) suggested by the pandas primer result. So perhaps we are not on the same page. I don't necessarily think suggesting the list approach is the solution to go for. Perhaps we need to suppress instead when there is a comprehension like Daniël mentioned. |
@nickdrozd your comment seems to indicate that you should have voted for considering it a false positive instead ? |
@Pierre-Sassoulas Yes, I got the options backwards. I want it to NOT suggest rewriting That said, there is one way make this snippet IMO a little easier to read: max(header_colwidth, max(self.adj.len(x) for x in fmt_values)) So there are nested |
The council has spoken unanimousely : it's a false positive. |
For now, I'm going to change the PR to not warn on the pattern anymore. The more intricate suggestion would require more time than I or the original author seems to have for this PR. I just want to get this in a minimal viable state and merge as I still think it would be useful in its minimal state! |
🤖 According to the primer, this change has no effect on the checked open source code. 🤖🎉 This comment was generated for commit 98b3408 |
Thank you for the initial implementation @osherdp, and congratulation on becoming a pylint contributor. Thank you for taking over @DanielNoord ! |
@Pierre-Sassoulas Please check the commit message before merging. I was just looking through the git log and saw that a new extension was added when in fact it's activated by default. I know these things are sometimes easy to miss 😅 bb5781c |
Good catch, that's my bad. I was thinking that we always add new things as extension now. I don't think it's fixable at this point :( |
towncrier create <IssueNumber>.<type>
which will beincluded in the changelog.
<type>
can be one of: new_check, removed_check, extension,false_positive, false_negative, bugfix, other, internal. If necessary you can write
details or offer examples on how the new change is supposed to work.
Type of Changes
Description
This adds a new checker (not active by default) which identifies usages similar to
min(<arg1>, min(<arg2>, <arg3>))
and suggests using a simplified form ofmin(<arg1>, <arg2>, <arg3>)
. Same goes formax
usage.The logic is as follows: it detects calls to either
min
ormax
functions, and whenever one of their arguments is that same function, it emits the message.Closes #7546