-
Notifications
You must be signed in to change notification settings - Fork 1.8k
[flake8-tidy-imports] Add TID253
#6378
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
Changes from all commits
48b134c
04fa4e8
51f2af3
07a26e3
17d5c9a
3725ca8
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,31 @@ | ||
| ## Banned modules ## | ||
| import torch | ||
|
|
||
| from torch import * | ||
|
|
||
| from tensorflow import a, b, c | ||
|
|
||
| import torch as torch_wearing_a_trenchcoat | ||
|
|
||
| # this should count as module level | ||
| x = 1; import tensorflow | ||
|
|
||
| # banning a module also bans any submodules | ||
| import torch.foo.bar | ||
|
|
||
| from tensorflow.foo import bar | ||
|
|
||
| from torch.foo.bar import * | ||
|
|
||
| # unlike TID251, inline imports are *not* banned | ||
| def my_cool_function(): | ||
| import tensorflow.foo.bar | ||
|
|
||
| def another_cool_function(): | ||
| from torch.foo import bar | ||
|
|
||
| def import_alias(): | ||
| from torch.foo import bar | ||
|
|
||
| if TYPE_CHECKING: | ||
| import torch |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,111 @@ | ||
| use ruff_diagnostics::{Diagnostic, Violation}; | ||
| use ruff_macros::{derive_message_formats, violation}; | ||
| use ruff_text_size::TextRange; | ||
|
|
||
| use crate::checkers::ast::Checker; | ||
|
|
||
| /// ## What it does | ||
| /// Checks for module-level imports that should instead be imported lazily | ||
| /// (e.g., within a function definition, or an `if TYPE_CHECKING:` block, or | ||
| /// some other nested context). | ||
| /// | ||
| /// ## Why is this bad? | ||
| /// Some modules are expensive to import. For example, importing `torch` or | ||
| /// `tensorflow` can introduce a noticeable delay in the startup time of a | ||
| /// Python program. | ||
| /// | ||
| /// In such cases, you may want to enforce that the module is imported lazily | ||
| /// as needed, rather than at the top of the file. This could involve inlining | ||
| /// the import into the function that uses it, rather than importing it | ||
| /// unconditionally, to ensure that the module is only imported when necessary. | ||
| /// | ||
| /// ## Example | ||
| /// ```python | ||
| /// import tensorflow as tf | ||
| /// | ||
| /// | ||
| /// def show_version(): | ||
| /// print(tf.__version__) | ||
| /// ``` | ||
| /// | ||
| /// Use instead: | ||
| /// ```python | ||
| /// def show_version(): | ||
| /// import tensorflow as tf | ||
| /// | ||
| /// print(tf.__version__) | ||
| /// ``` | ||
| /// | ||
| /// ## Options | ||
| /// - `flake8-tidy-imports.banned-module-level-imports` | ||
| #[violation] | ||
| pub struct BannedModuleLevelImports { | ||
| name: String, | ||
| } | ||
|
|
||
| impl Violation for BannedModuleLevelImports { | ||
| #[derive_message_formats] | ||
| fn message(&self) -> String { | ||
| let BannedModuleLevelImports { name } = self; | ||
| format!("`{name}` is banned at the module level") | ||
| } | ||
| } | ||
|
|
||
| /// TID253 | ||
| pub(crate) fn name_is_banned_at_module_level( | ||
| checker: &mut Checker, | ||
| name: &str, | ||
| text_range: TextRange, | ||
| ) { | ||
| banned_at_module_level_with_policy(checker, name, text_range, &NameMatchPolicy::ExactOnly); | ||
| } | ||
|
|
||
| /// TID253 | ||
| pub(crate) fn name_or_parent_is_banned_at_module_level( | ||
| checker: &mut Checker, | ||
| name: &str, | ||
| text_range: TextRange, | ||
| ) { | ||
| banned_at_module_level_with_policy(checker, name, text_range, &NameMatchPolicy::ExactOrParents); | ||
| } | ||
|
|
||
| #[derive(Debug)] | ||
| enum NameMatchPolicy { | ||
| /// Only match an exact module name (e.g., given `import foo.bar`, only match `foo.bar`). | ||
| ExactOnly, | ||
| /// Match an exact module name or any of its parents (e.g., given `import foo.bar`, match | ||
| /// `foo.bar` or `foo`). | ||
| ExactOrParents, | ||
| } | ||
|
|
||
| fn banned_at_module_level_with_policy( | ||
| checker: &mut Checker, | ||
| name: &str, | ||
| text_range: TextRange, | ||
| policy: &NameMatchPolicy, | ||
| ) { | ||
| if !checker.semantic().at_top_level() { | ||
| return; | ||
| } | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think more robust here would be to use x = 1; import tensorflow
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thanks! I shamelessly copied this from the
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thanks for pointing that out, good call! Fixed in https://github.com/astral-sh/ruff/pull/6526/files. |
||
| let banned_module_level_imports = &checker | ||
| .settings | ||
| .flake8_tidy_imports | ||
| .banned_module_level_imports; | ||
| for banned_module_name in banned_module_level_imports { | ||
| let name_is_banned = match policy { | ||
| NameMatchPolicy::ExactOnly => name == banned_module_name, | ||
| NameMatchPolicy::ExactOrParents => { | ||
| name == banned_module_name || name.starts_with(&format!("{banned_module_name}.")) | ||
| } | ||
| }; | ||
| if name_is_banned { | ||
| checker.diagnostics.push(Diagnostic::new( | ||
| BannedModuleLevelImports { | ||
| name: banned_module_name.to_string(), | ||
| }, | ||
| text_range, | ||
| )); | ||
| return; | ||
| } | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,5 +1,7 @@ | ||
| pub(crate) use banned_api::*; | ||
| pub(crate) use banned_module_level_imports::*; | ||
| pub(crate) use relative_imports::*; | ||
|
|
||
| mod banned_api; | ||
| mod banned_module_level_imports; | ||
| mod relative_imports; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,81 @@ | ||
| --- | ||
| source: crates/ruff/src/rules/flake8_tidy_imports/mod.rs | ||
| --- | ||
| TID253.py:2:8: TID253 `torch` is banned at the module level | ||
| | | ||
| 1 | ## Banned modules ## | ||
| 2 | import torch | ||
| | ^^^^^ TID253 | ||
| 3 | | ||
| 4 | from torch import * | ||
| | | ||
|
|
||
| TID253.py:4:1: TID253 `torch` is banned at the module level | ||
| | | ||
| 2 | import torch | ||
| 3 | | ||
| 4 | from torch import * | ||
| | ^^^^^^^^^^^^^^^^^^^ TID253 | ||
| 5 | | ||
| 6 | from tensorflow import a, b, c | ||
| | | ||
|
|
||
| TID253.py:6:1: TID253 `tensorflow` is banned at the module level | ||
| | | ||
| 4 | from torch import * | ||
| 5 | | ||
| 6 | from tensorflow import a, b, c | ||
| | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ TID253 | ||
| 7 | | ||
| 8 | import torch as torch_wearing_a_trenchcoat | ||
| | | ||
|
|
||
| TID253.py:8:8: TID253 `torch` is banned at the module level | ||
| | | ||
| 6 | from tensorflow import a, b, c | ||
| 7 | | ||
| 8 | import torch as torch_wearing_a_trenchcoat | ||
| | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ TID253 | ||
| 9 | | ||
| 10 | # this should count as module level | ||
| | | ||
|
|
||
| TID253.py:11:15: TID253 `tensorflow` is banned at the module level | ||
| | | ||
| 10 | # this should count as module level | ||
| 11 | x = 1; import tensorflow | ||
| | ^^^^^^^^^^ TID253 | ||
| 12 | | ||
| 13 | # banning a module also bans any submodules | ||
| | | ||
|
|
||
| TID253.py:14:8: TID253 `torch` is banned at the module level | ||
| | | ||
| 13 | # banning a module also bans any submodules | ||
| 14 | import torch.foo.bar | ||
| | ^^^^^^^^^^^^^ TID253 | ||
| 15 | | ||
| 16 | from tensorflow.foo import bar | ||
| | | ||
|
|
||
| TID253.py:16:1: TID253 `tensorflow` is banned at the module level | ||
| | | ||
| 14 | import torch.foo.bar | ||
| 15 | | ||
| 16 | from tensorflow.foo import bar | ||
| | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ TID253 | ||
| 17 | | ||
| 18 | from torch.foo.bar import * | ||
| | | ||
|
|
||
| TID253.py:18:1: TID253 `torch` is banned at the module level | ||
| | | ||
| 16 | from tensorflow.foo import bar | ||
| 17 | | ||
| 18 | from torch.foo.bar import * | ||
| | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ TID253 | ||
| 19 | | ||
| 20 | # unlike TID251, inline imports are *not* banned | ||
| | | ||
|
|
||
|
|
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
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 needs an example
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.
Added!