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 #85, #2615: Emit used-before-assignment in final or except blocks where try statements could have failed #5384

Conversation

jacobtylerwalls
Copy link
Member

@jacobtylerwalls jacobtylerwalls commented Nov 24, 2021

Type of Changes

Type
✨ New feature

Description

Previously
Pylint assumed assignments in the Try block of a Try/Finally or Try/Except pair would succeed. In cases of failure, though, attempting to use such names in a final or except block would raise UnboundLocalError.

Now
Pylint emits used-before-assignment to account for the fact that the statement might have been interrupted by an exception.

Closes #85, Closes #2615

@jacobtylerwalls jacobtylerwalls force-pushed the try-finally-used-before-assignment branch from da2aefa to df5c775 Compare November 24, 2021 17:22
@DanielNoord
Copy link
Collaborator

I actually had an interesting discussion about this with one of the maintainers of pylance:
microsoft/pylance-release#1958

Basically it comes down to whether we want to assume all try statement fail or all succeed.

Personally I'm inclined to say let's assume it is succeeds. As a user I will be more annoyed by having to add disable's everywhere because one of my tools is producing false positives compared to running into exceptions because my tools are too "dumb". I prefer dumbness over potentially incorrect strictness, but this might be personal preference.

@coveralls
Copy link

coveralls commented Nov 24, 2021

Pull Request Test Coverage Report for Build 1570790157

  • 13 of 13 (100.0%) changed or added relevant lines in 1 file are covered.
  • 21 unchanged lines in 2 files lost coverage.
  • Overall coverage increased (+0.01%) to 93.714%

Files with Coverage Reduction New Missed Lines %
pylint/checkers/refactoring/refactoring_checker.py 2 98.16%
pylint/extensions/_check_docs_utils.py 19 92.66%
Totals Coverage Status
Change from base Build 1566586190: 0.01%
Covered Lines: 14192
Relevant Lines: 15144

💛 - Coveralls

@jacobtylerwalls
Copy link
Member Author

Thanks for the speedy reply!

As a user I will be more annoyed by having to add disable's everywhere because one of my tools is producing false positives

So my suggestion here for the user would be to just define the variables before the try rather than add disables.

Basically it comes down to whether we want to assume all try statement fail or all succeed.

I agree this is a design question. I think this approach splits the difference well. We assume the try statements fail for the sake of finally statements relying on names that might not exist, which is easy for users to fix by initializing their variables, but we assume the try statements succeed for the sake of evaluating unused-variable, which I would agree would be quite annoying to disable if the variable is used later in the try block. How does that strike you?

I know it's not surveying opinions on the other side very well, but there's 13 +1's on the ticket. I tend to agree with the Pylance maintainer that if you're depending on a name in the finally block, then that really is an error--either something should be moved out of the try, or else the variable should be initialized before the try.

@Pierre-Sassoulas
Copy link
Member

If you are confident that a statement will never raise an exception, you can move it outside of the try block

I found this argument from Eric Traut pretty convincing, honestly. By adding a statement in a Try except the user tell us that the code can fail so we should probably treat the code as possibly failing. If something never fail it should be outside the try and the resulting code will be clearer and better.

@DanielNoord
Copy link
Collaborator

Does it make sense to create "possibly-used-before-assignment" for this? Like we have "possibly-unused-variable"? I think @Pierre-Sassoulas and I discussed at some point prior.

If not, I think we should go with the majority opinion and continue with this PR!

@jacobtylerwalls jacobtylerwalls force-pushed the try-finally-used-before-assignment branch from df5c775 to eba3d3c Compare November 25, 2021 15:07
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.

LGTM,! Thank you. I think that taking the same decision than pylance is the safe bet here. But I'm putting this in 2.13 so opinion can be heard about the controversial part of the issue if required.

@Pierre-Sassoulas Pierre-Sassoulas added this to the 2.13.0 milestone Nov 25, 2021
@Pierre-Sassoulas Pierre-Sassoulas added Enhancement ✨ Improvement to a component Control flow Requires control flow understanding labels Nov 25, 2021
Copy link
Collaborator

@DanielNoord DanielNoord left a comment

Choose a reason for hiding this comment

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

Small changes here and there! Rest looks good! Glad to see such an old issue finally resolved even if I did not initially agree with the solution. I'm must admit I do think this might be the best solution for this problem going forward. Thanks @jacobtylerwalls for putting this to our attention! 😄

ChangeLog Outdated Show resolved Hide resolved
pylint/checkers/variables.py Outdated Show resolved Hide resolved
tests/functional/u/use/used_before_assignment_issue85.py Outdated Show resolved Hide resolved
@jacobtylerwalls
Copy link
Member Author

Let's not merge until I solve #2615 with a very similar block. I think I can use the same branch.

@jacobtylerwalls jacobtylerwalls changed the title Fix #85: Emit used-before-assignment in final blocks where try statements could have failed Fix #85, #2615: Emit used-before-assignment in final or except blocks where try statements could have failed Nov 26, 2021
pylint/checkers/variables.py Outdated Show resolved Hide resolved
pylint/checkers/variables.py Outdated Show resolved Hide resolved
@Pierre-Sassoulas Pierre-Sassoulas added Blocked 🚧 Blocked by a particular issue and removed Blocked 🚧 Blocked by a particular issue labels Dec 7, 2021
@Pierre-Sassoulas
Copy link
Member

@jacobtylerwalls , we merged #5402 (🎉), but I'm not sure how to resolve the conflict, could you rebase on the latest main, please ?

…final or except blocks where try statements could have failed
@jacobtylerwalls jacobtylerwalls force-pushed the try-finally-used-before-assignment branch from e9ffbd5 to 228f56f Compare December 11, 2021 14:09
Copy link
Collaborator

@DanielNoord DanielNoord left a comment

Choose a reason for hiding this comment

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

Thanks! Most of this already looks good.

I thought about node.statement previously but never commented about it. Upon re-review I think it makes sense.

pylint/checkers/variables.py Outdated Show resolved Hide resolved
pylint/checkers/variables.py Outdated Show resolved Hide resolved
@@ -405,7 +405,8 @@ def _has_locals_call_after_node(stmt, scope):
"E0601": (
"Using variable %r before assignment",
"used-before-assignment",
"Used when a local variable is accessed before its assignment.",
"Used when a local variable is accessed before its assignment or within "
Copy link
Collaborator

Choose a reason for hiding this comment

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

Let's merge this in the current state and use #5506 to perfect this description 😄

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Control flow Requires control flow understanding Enhancement ✨ Improvement to a component
Projects
None yet
4 participants