[pylint] Ignore __init__.py files in (PLC0414) (#18400)

## Summary

Ignore `__init__.py` files in `useless-import-alias` (PLC0414).
See discussion in #18365 and #6294: we want to allow redundant aliases
in `__init__.py` files, as they're almost always intentional explicit
re-exports.
Closes #18365
 Closes #6294

---------

Co-authored-by: Dylan <dylwil3@gmail.com>
This commit is contained in:
Gideon 2025-06-20 20:20:27 +02:00 committed by GitHub
parent 8cff77c82e
commit 2910988b06
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 66 additions and 0 deletions

View file

@ -0,0 +1,4 @@
import collections as collections
from collections import OrderedDict as OrderedDict
from . import foo as foo
from .foo import bar as bar

View file

@ -77,3 +77,10 @@ pub(crate) const fn is_multiple_with_statements_fix_safe_enabled(
) -> bool {
settings.preview.is_enabled()
}
// https://github.com/astral-sh/ruff/pull/18400
pub(crate) const fn is_ignore_init_files_in_useless_alias_enabled(
settings: &LinterSettings,
) -> bool {
settings.preview.is_enabled()
}

View file

@ -168,6 +168,7 @@ mod tests {
)]
#[test_case(Rule::UselessElseOnLoop, Path::new("useless_else_on_loop.py"))]
#[test_case(Rule::UselessImportAlias, Path::new("import_aliasing.py"))]
#[test_case(Rule::UselessImportAlias, Path::new("import_aliasing_2/__init__.py"))]
#[test_case(Rule::UselessReturn, Path::new("useless_return.py"))]
#[test_case(Rule::UselessWithLock, Path::new("useless_with_lock.py"))]
#[test_case(Rule::UnreachableCode, Path::new("unreachable.py"))]
@ -418,6 +419,19 @@ mod tests {
Ok(())
}
#[test]
fn preview_useless_import_alias() -> Result<()> {
let diagnostics = test_path(
Path::new("pylint/import_aliasing_2/__init__.py"),
&LinterSettings {
preview: PreviewMode::Enabled,
..LinterSettings::for_rule(Rule::UselessImportAlias)
},
)?;
assert_diagnostics!(diagnostics);
Ok(())
}
#[test]
fn import_outside_top_level_with_banned() -> Result<()> {
let diagnostics = test_path(

View file

@ -4,11 +4,14 @@ use ruff_macros::{ViolationMetadata, derive_message_formats};
use ruff_text_size::Ranged;
use crate::checkers::ast::Checker;
use crate::preview::is_ignore_init_files_in_useless_alias_enabled;
use crate::{Edit, Fix, FixAvailability, Violation};
/// ## What it does
/// Checks for import aliases that do not rename the original package.
///
/// In [preview] this rule does not apply in `__init__.py` files.
///
/// ## Why is this bad?
/// The import alias is redundant and should be removed to avoid confusion.
///
@ -32,6 +35,8 @@ use crate::{Edit, Fix, FixAvailability, Violation};
/// ```python
/// import numpy
/// ```
///
/// [preview]: https://docs.astral.sh/ruff/preview/
#[derive(ViolationMetadata)]
pub(crate) struct UselessImportAlias {
required_import_conflict: bool,
@ -67,6 +72,14 @@ pub(crate) fn useless_import_alias(checker: &Checker, alias: &Alias) {
if alias.name.as_str() != asname.as_str() {
return;
}
// A re-export in __init__.py is probably intentional.
if checker.path().ends_with("__init__.py")
&& is_ignore_init_files_in_useless_alias_enabled(checker.settings)
{
return;
}
// A required import with a useless alias causes an infinite loop.
// See https://github.com/astral-sh/ruff/issues/14283
let required_import_conflict = checker
@ -100,6 +113,12 @@ pub(crate) fn useless_import_from_alias(
if alias.name.as_str() != asname.as_str() {
return;
}
// A re-export in __init__.py is probably intentional.
if checker.path().ends_with("__init__.py") {
return;
}
// A required import with a useless alias causes an infinite loop.
// See https://github.com/astral-sh/ruff/issues/14283
let required_import_conflict = checker.settings.isort.requires_member_import(

View file

@ -0,0 +1,18 @@
---
source: crates/ruff_linter/src/rules/pylint/mod.rs
---
__init__.py:1:8: PLC0414 [*] Import alias does not rename original package
|
1 | import collections as collections
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ PLC0414
2 | from collections import OrderedDict as OrderedDict
3 | from . import foo as foo
|
= help: Remove import alias
Unsafe fix
1 |-import collections as collections
1 |+import collections
2 2 | from collections import OrderedDict as OrderedDict
3 3 | from . import foo as foo
4 4 | from .foo import bar as bar

View file

@ -0,0 +1,4 @@
---
source: crates/ruff_linter/src/rules/pylint/mod.rs
---