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

New check simplifiable-condition and add bool() check for len-as-condition #3644

Merged
merged 2 commits into from
Aug 31, 2020

Conversation

ethan-leba
Copy link
Contributor

@ethan-leba ethan-leba commented May 24, 2020

Steps

  • Add yourself to CONTRIBUTORS if you are a new contributor.
  • Add a ChangeLog entry describing what your PR does.
  • If it's a new feature or an important bug fix, add a What's New entry in doc/whatsnew/<current release.rst>.
  • Write a good description on what the PR does.

Description

  • Added check for bool(len(x)) to len-as-condition.
  • Added simplifiable-condition check to the refactoring checker, which will suggest removing constants that have no effect on a boolean condition, i.e. False or x -> x, and replacing conditions with a constant if there is a shortcircuiting value, i.e. True or x -> True.

Type of Changes

Type
🐛 Bug fix
✨ New feature
🔨 Refactoring
📜 Docs

Related Issue

Closes #3407

@coveralls
Copy link

coveralls commented May 24, 2020

Coverage Status

Coverage increased (+0.03%) to 90.726% when pulling 7dd2ae5 on ethan-leba:simplifiable-condition into 21d168a on PyCQA:master.

@ethan-leba ethan-leba marked this pull request as ready for review May 24, 2020 15:59
@PCManticore
Copy link
Contributor

Hey @ethan-leba Thanks for the PR. Can you split this into three separate PRs? As it stands now, it includes three changes that are easier to review separately.

@ethan-leba ethan-leba force-pushed the simplifiable-condition branch 2 times, most recently from 961974b to 748657d Compare June 8, 2020 12:05
@ethan-leba
Copy link
Contributor Author

Definitely agree this PR is a little overloaded, but the reason it ended up like that is because the main commit containing the checker depends on refactoring changes made in the first. I removed the last commit, and will PR it separately if/when the other changes get in, but I think the first two commits are too coupled to split into separate PRs.

Copy link
Contributor

@PCManticore PCManticore left a comment

Choose a reason for hiding this comment

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

Hey @ethan-leba Looking great, thanks for the work. Left several comments we should check out before we can pull it in.

pylint/checkers/refactoring.py Outdated Show resolved Hide resolved
pylint/checkers/refactoring.py Outdated Show resolved Hide resolved
assert False and False # [simplifiable-condition]

# Expressions not in one of the above situations will not emit a message
CONSTANT or True
Copy link
Contributor

Choose a reason for hiding this comment

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

Let's add a test for something that is not uninferable:

Suggested change
CONSTANT or True
from unknown import Unknown
bool(Unknown or True) # should not emit

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Shouldn't bool(Unknown or True) emit an error, as it can be simplified to True?

Copy link
Contributor

Choose a reason for hiding this comment

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

Not quite, as we do not know what Unknown actually is at runtime.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

But in this case the value of Unknown shouldn't matter:

>>> Unknown = False
>>> bool(Unknown or True)
True
>>> Unknown = True
>>> bool(Unknown or True)
True
>>> Unknown = 123
>>> bool(Unknown or True)
True

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The reason why CONSTANT or True in the test-case above doesn't emit an error is b/c it's not in a condition, so the value of CONSTANT affects the result, for example:

>>> CONSTANT = 0
>>> CONSTANT or True
True
>>> CONSTANT = -134
>>> CONSTANT or True
-134

Just wanted to point that out in case that was the source of confusion!

Copy link
Contributor Author

@ethan-leba ethan-leba Aug 9, 2020

Choose a reason for hiding this comment

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

Can I move forward with splitting the checker, or should I wait for PCManticore's input on this? I don't think it should be too difficult.

Copy link
Member

Choose a reason for hiding this comment

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

Yeah I think you can go on and do it. Looking at @PCManticore profile he seems to be quite busy with "not gihub thing" lately and he's probably spammed with other github issues anyway. With two positives review + this comment I'm confident enough to merge myself.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Okay, sounds good! Just one question about suspicious-boolean-condition, should this checker target single constants in conditionals, i.e. if True: or bool(True)? Currently this checker would only emit a message if the expression can be reduced to a single constant, but not if the expression is a single constant.

Copy link
Member

Choose a reason for hiding this comment

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

Hmm, yes probably. If it's really hard to disentangle the code, we can probably create an issue to do it separately.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

using-constant-test should cover the case of single constants mentioned above

tests/functional/s/simplifiable_condition.py Show resolved Hide resolved
pylint/checkers/utils.py Outdated Show resolved Hide resolved
@ethan-leba ethan-leba changed the title New check simplifiable-condition, add bool() check for len-as-condition, and contextual messages for singleton-comparison New check simplifiable-condition and add bool() check for len-as-condition Jun 9, 2020
@ethan-leba
Copy link
Contributor Author

Thanks for the review @PCManticore! Revised the PR, just left one question above

@ethan-leba ethan-leba force-pushed the simplifiable-condition branch 2 times, most recently from f322004 to a42dd91 Compare June 12, 2020 00:25
Copy link
Member

@Pierre-Sassoulas Pierre-Sassoulas left a comment

Choose a reason for hiding this comment

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

Very nice feature!

pylint/checkers/refactoring.py Show resolved Hide resolved
@ethan-leba
Copy link
Contributor Author

Anything else that needs to be done on my part to move this PR forward @PCManticore ?

@Pierre-Sassoulas
Copy link
Member

It seems like there is an ongoing discussion about the message of the recommendation that could be made better, but I'll let @PCManticore confirm if I understood correctly when he's available. In the meantime could you rebase on the latest master, please?

@ethan-leba ethan-leba force-pushed the simplifiable-condition branch 3 times, most recently from 8eac6d4 to 4c28ea0 Compare August 30, 2020 18:31
@ethan-leba
Copy link
Contributor Author

I've split off simplification to a constant to a separate check: condition-evals-to-constant. LMK if anything else needs to be done 👍

Copy link
Member

@Pierre-Sassoulas Pierre-Sassoulas left a comment

Choose a reason for hiding this comment

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

This look clean and well tested, thank you ! But I wonder what is the difference between simplifiable condition and condition eval to constant. Because the two functional test files look the same (with False and True inverted) and gives a very different result. Why is bool(CONSTANT or False) # [simplifiable-condition] and bool(CONSTANT or True) # [condition-evals-to-constant] ? What is the difference between the two ?

@ethan-leba
Copy link
Contributor Author

simplifiable-condition is when constants can be removed from an and/or in a condition, condition-evals-to-constant is when an and/or can be simplified in a way that it no longer contains any variables. We discussed this in this thread: #3644 (comment)

CONSTANT or False -> CONSTANT
CONSTANT or True -> True

@Pierre-Sassoulas
Copy link
Member

Sorry, it makes sense (again) now. Thank you for explaining it another time and thank you for this very nice addition to Pylint. Simplifying boolean conditions is always very satisfying and useful.

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.

Simplify Boolean expressions
4 participants