-
-
Notifications
You must be signed in to change notification settings - Fork 1.1k
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: import-outside-toplevel (close #3067) #3079
New check: import-outside-toplevel (close #3067) #3079
Conversation
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.
Looks good to me!
c9b2d21
to
514a5ed
Compare
There's a false negative for this check: def k():
for _ in range(5):
import sys # should trigger, but doesn't Is there a good way to detect this situation? |
514a5ed
to
147f4f7
Compare
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.
Having looked at this again, please could we fix the false negative before merging?
pylint/checkers/imports.py
Outdated
@@ -969,6 +976,14 @@ def _wildcard_import_is_allowed(self, imported_module): | |||
and "__all__" in imported_module.locals | |||
) | |||
|
|||
def _check_toplevel(self, node): | |||
if isinstance(node.parent, (astroid.FunctionDef, astroid.ClassDef)): |
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 you use node.scope()
here, it will traverse up the syntax tree until it finds the node that defines the scope (https://astroid.readthedocs.io/en/latest/api/base_nodes.html#astroid.node_classes.NodeNG.scope). Anything other than a Module
would mean that the message should be raised. This should even account for cases like the following where we wouldn't want to raise a message:
if some_condition:
import this
else:
import that
and cases like the following where we would want to raise the message:
def func():
if some_condition:
import this
...
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 knew there was something like that, but I couldn't remember the name. This should be fixed now.
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.
Overall this looks solid, thanks @nickdrozd !
One minor suggestion before getting this in. Sometimes some imports need to be scoped to a function scope in case they are related to a circular import between two components. In that case, we'd need a csv option for allowing certain imports to be exempted from this check when encountered. This would avoid a proliferation of # disable: import-outside-toplevel
for such cases.
147f4f7
to
456cb9e
Compare
It seems to work! |
456cb9e
to
fdb908f
Compare
@PCManticore You mean like a whitelist of modules that can be imported outside the toplevel? I have no idea how to implement that. Are there any similar examples? |
@nickdrozd Yes, something along those lines. This would involve:
|
fdb908f
to
a6a6141
Compare
@AWhetter @PCManticore Okay, I think that should do it. I added some unit tests as well. |
a6a6141
to
c041cc4
Compare
c041cc4
to
263b8b1
Compare
Thank you @nickdrozd ! |
Steps
doc/whatsnew/<current release.rst>
.Description
This check warns when modules are imported from places other than a
module toplevel, e.g. inside a function or a class.
Type of Changes