-
-
Notifications
You must be signed in to change notification settings - Fork 1.2k
fix: support block-level wrong-import-position pragma suppression #10590
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
base: main
Are you sure you want to change the base?
Conversation
Codecov Reportβ
All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #10590 +/- ##
==========================================
+ Coverage 95.95% 95.97% +0.01%
==========================================
Files 176 176
Lines 19455 19537 +82
==========================================
+ Hits 18668 18750 +82
Misses 787 787
π New features to boost your workflow:
|
This comment has been minimized.
This comment has been minimized.
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'm wondering if the 'right way to fix this' isn't to return uninferable in astroid instead. Because it's possible that a function validly return an empty list.
@Pierre-Sassoulas Should it be fixed in the astroid library itself or using an astroid hook in pylint library? |
Astroid itself, we're the same maintainers and 99% of astroid user are also pylint users :) |
Signed-off-by: Emmanuel Ferdman <[email protected]>
28128d9
to
8cace41
Compare
Signed-off-by: Emmanuel Ferdman <[email protected]>
dd9aafb
to
a7e937a
Compare
for more information, see https://pre-commit.ci
@Pierre-Sassoulas thanks for the guidance! I opened a PR in the astroid repo for this issue. Since this PR is already open, I also included a fix for another pylint issue. Apologies for the force push π |
This comment has been minimized.
This comment has been minimized.
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 the PR!
In the future it would be better to open a new one as that makes communication scoped a bit better. We can still continue with this one though :)
import json # pylint: disable=wrong-import-position | ||
|
||
# Test that subsequent imports are not affected by the pragma above | ||
import csv # [wrong-import-position] | ||
import re # [wrong-import-position] |
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'm not sure if this is the correct behaviour, or at least should be considered a breaking change?
The test you're changing above seems to explicitly test for this case:
A previous disable should also count for subsequent imports.
@Pierre-Sassoulas What do you think?
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.
Ha, yes, we reviewed independently but I think we said the same thing..
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.
@Pierre-Sassoulas You approved, but this question still stands I guess? We're now actively removing support for behaviour for which we had a test. I'm not sure why we added this behaviour in the first place, but considering we are changing behaviour isn't this a breaking change?
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.
Right, we didn't say the same thing after all. It seems we can't have an easy implementation of both "disable on a statement" and "any disable prevent all further wrong-import-position" so there's a decision to take here. And we might have to do the hard implementation, especially since I don't think isort or ruff deal with intertwined statements in imports (?).
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.
Current PR does:
The fix changes the message enablement check from using the first non-import node's line number to using the current import node's line number, ensuring proper pragma isolation.
Perhaps we can it to:
The fix changes the message enablement check from no longer working on non-import statements for this message. A pragma will disable from the current import statement until further statements, ensuring proper pragma isolation.
pylint/checkers/imports.py
Outdated
if self.linter.is_message_enabled( | ||
"wrong-import-position", self._first_non_import_node.fromlineno | ||
): | ||
if self.linter.is_message_enabled("wrong-import-position", node.fromlineno): |
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.
Instinctively I would say the fix need to be a lot more complex than that. If one line is disabled it means not only that the message shouldn't be raised for it but also that other line should not raise if the only issue for them it the disabled line.
i.e. the expected is
import a
import b
import c
import b # [wrong-import-position]
import a # [wrong-import-position]
import c
import b # pylint: disable=wrong-import-position
import a
import c
import b # pylint: disable=wrong-import-position
import c # [wrong-import-position]
import a # [wrong-import-position]
Tbf, I don't think we should sweat other this, maybe we need to abandon the pylint disable system for this specific error message, and take the isort/ruff "I" disable into account, or drop the message entirely, or integrate ruff plus autofix and drop isort, or integrate both ruff and isort... I don't see a fix for this being super impactful in the mid term.
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 don't fully understand your comment, and I think taking into accounts other tools setting is bit too much work for @emmanuel-ferdman π
@emmanuel-ferdman Do you think we can fix your issue while still keeping the behaviour as being tested in the test? I agree that a disable on a non-import makes no sense, but perhaps we can fix it with a smaller scope?
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.
What I mean is that the effort to fix this at all is not worth it as we have ruff and isort for reordering import, having working disable in pylint or coordinating multiple tools that check this message does not bring a lot of value imo. (Maybe we can even remove the check entirely for 4.0)
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.
@DanielNoord @Pierre-Sassoulas Thanks for the feedback π
From issue #10589 and seeing that other pylint rules are line-based, I assumed this was a bug. But I understand that wrong-import-position
might be different since it's about import ordering patterns. What should we do here? I'm happy to try and implement whatever you think is best.
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.
Sorry for marking an issue as ready for PR when clearly it wasn't well specified @emmanuel-ferdman. Or thank you for making the fix and making us realize what actually fixing this bug imply. I think we should close because there's probably better use for your time. Right now I'm more and more convinced that we should either drop isort and wrong-import-position (import time at startup could make this a big change for users) or add ruff (using ruff and isort as optional dependencies instead of isort as mandatory dependency would open a world of possibilities once the wrong-import-position
proof of concept is validated). In all case this mean this fix is not going to be used much. Need some other maintainers opinion about it and it's probably an idea for pylint 5.0 anyway (that I should open an issue for if Daniel don't think it's trash).
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 agree with removing the dependency on isort
.
It fails logical to let those concerns be dealt with by a separate tool with its own configuration and/or ignores. Wondering what other maintainers think.
@Pierre-Sassoulas, you gave 4 examples above (importing a, b, & c). The original bug report is about wrong-import-position, which maybe doesn't require sorting & other complexities? At any rate, thanks for taking my report seriously, I appreciate that! :) |
Ha, indeed you're right, my bad. Well the fix LGTM as is after a second take. I'm going to create an issue for the unrelated discussion. |
Sorry to risk additional confusion. I will try hard to be clear. However, during the above conversation, I agreed with the expectations of a different condition, C0411 wrong-import-order, as shown with importing a, b, & c in different orders.
which seemed reasonable. But OTOH, this also seems reasonable to the user:
So, which is actually implemented? Easy to check...
I DO get C0411 when I, for example, import a third-party lib in the wrong place.
Maybe C0411 doesn't attempt to validate the "alphabetical" sort order recommendation of PEP 8, and concerns itself only with whether the three sets of imports are correctly organized per PEP 8 like (1) standard lib imports, (2) related 3rd party lib imports (3) Local application/library specific imports. |
@Pierre-Sassoulas @DanielNoord Thanks for the review! Just checking what the final decision is - should this be merged, closed, or is any further action needed? |
Sorry for the delay @emmanuel-ferdman we released 4.0 :) did you consider @ruck94301 's point ? What do you think ? |
@Pierre-Sassoulas I tested all the examples @ruck94301 mentioned. They were just clarifying how C0411 works separately from C0413. The current PR changes fulfill what's needed - the pragma now works line-by-line for C0413 as expected, and C0411 is unaffected. Ready to review/merge from my side. |
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'm not sure the fix in its current form is a net positive, what's your opinion @DanielNoord ?
CONSTANT = True # pylint: disable=wrong-import-position | ||
|
||
import sys | ||
import sys # [wrong-import-position] |
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 disabling on the line that create the issue instead of the multiple imports that are affected, made sense and was instinctive. I would test for this:
import sys # [wrong-import-position] | |
import os | |
import sys | |
CONSTANT = False # pylint: disable=wrong-import-position | |
import time | |
SOME_OTHER_CONSTANT = True | |
import logging # [wrong-import-position] |
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 I understand now - You suggest putting the pragma on the non-import line that breaks the import block, so it suppresses the following imports until the next non-import. Inline disables on the import line should still work. My new changes follow this logic - please let me know if that what you meant.
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.
This still seems like a breaking change?
Note that I won't have a lot of internet access in the coming weeks. So don't block this on my review.
We're removing a dubious feature. Maybe we can avoid backporting in that case. It was added in #1337 without much discussion. |
Signed-off-by: Emmanuel Ferdman <[email protected]>
for more information, see https://pre-commit.ci
For now, I followed @Pierre-Sassoulasβs suggestion: a pragma on the non-import line that breaks the import block suppresses the following imports until the next non-import. Inline disables on the import line still work as before. Please let me know what you think. Example: import os
import sys
# First non-import with pragma - suppresses following imports
CONSTANT1 = "value1" # pylint: disable=wrong-import-position
import json
import csv
# Second non-import without pragma - imports after this should be flagged
CONSTANT2 = "value2"
import logging
# Third non-import with pragma - suppresses following imports again
CONSTANT3 = "value3" # pylint: disable=wrong-import-position
import pathlib
import random
# Fourth non-import without pragma - import after this should be flagged
CONSTANT4 = "value4"
import re Output:
|
Signed-off-by: Emmanuel Ferdman <[email protected]>
π€ According to the primer, this change has no effect on the checked open source code. π€π This comment was generated for commit 7fb7b18 |
Type of Changes
Description
Previously, a
# pylint: disable=wrong-import-position
pragma on a non-import statement would incorrectly suppress wrong-import-position messages for all subsequent import statements in the module. This violated the expected behavior where single-line pragmas should only affect their own line. The fix changes the message enablement check from using the first non-import node's line number to using the current import node's line number, ensuring proper pragma isolation.Example:
Expected Output:
wrong-import-position
messages (incorrect behavior)wrong-import-position
messages for bothimport os
andimport sys
(correct behavior)Closes #10589