[pyupgrade] Prevent infinite loop with I002 and UP026 (#20634)
Some checks are pending
CI / Determine changes (push) Waiting to run
CI / cargo fmt (push) Waiting to run
CI / cargo clippy (push) Blocked by required conditions
CI / cargo test (linux) (push) Blocked by required conditions
CI / cargo test (linux, release) (push) Blocked by required conditions
CI / cargo test (windows) (push) Blocked by required conditions
CI / cargo test (wasm) (push) Blocked by required conditions
CI / cargo build (release) (push) Waiting to run
CI / cargo build (msrv) (push) Blocked by required conditions
CI / cargo fuzz build (push) Blocked by required conditions
CI / fuzz parser (push) Blocked by required conditions
CI / test scripts (push) Blocked by required conditions
CI / ecosystem (push) Blocked by required conditions
CI / Fuzz for new ty panics (push) Blocked by required conditions
CI / cargo shear (push) Blocked by required conditions
CI / python package (push) Waiting to run
CI / pre-commit (push) Waiting to run
CI / mkdocs (push) Waiting to run
CI / formatter instabilities and black similarity (push) Blocked by required conditions
CI / test ruff-lsp (push) Blocked by required conditions
CI / check playground (push) Blocked by required conditions
CI / benchmarks instrumented (ruff) (push) Blocked by required conditions
CI / benchmarks instrumented (ty) (push) Blocked by required conditions
CI / benchmarks-walltime (push) Blocked by required conditions
[ty Playground] Release / publish (push) Waiting to run

## Summary
Closes #20601

Do not treat imports as unused for the rule [unnecessary-builtin-import
(UP029)](https://docs.astral.sh/ruff/rules/unnecessary-builtin-import/)
if they are required by
`isort`([missing-required-import](https://docs.astral.sh/ruff/rules/missing-required-import/))

## Test Plan
- Added test case `i002_up029_conflict` to ensure there is no conflict

Co-authored-by: Igor Drokin <drokinii1017@gmail.com>
This commit is contained in:
Igor Drokin 2025-10-01 00:11:34 +03:00 committed by GitHub
parent 7fee877c50
commit 11dae2cf1b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 53 additions and 5 deletions

View file

@ -0,0 +1 @@
from builtins import str, int

View file

@ -97,7 +97,7 @@ mod tests {
)]
#[test_case(Rule::UTF8EncodingDeclaration, Path::new("UP009_many_empty_lines.py"))]
#[test_case(Rule::UnicodeKindPrefix, Path::new("UP025.py"))]
#[test_case(Rule::UnnecessaryBuiltinImport, Path::new("UP029.py"))]
#[test_case(Rule::UnnecessaryBuiltinImport, Path::new("UP029_0.py"))]
#[test_case(Rule::UnnecessaryClassParentheses, Path::new("UP039.py"))]
#[test_case(Rule::UnnecessaryDefaultTypeArgs, Path::new("UP043.py"))]
#[test_case(Rule::UnnecessaryEncodeUTF8, Path::new("UP012.py"))]
@ -357,6 +357,32 @@ mod tests {
");
}
#[test_case(Path::new("UP029_1.py"))]
fn i002_up029_conflict(path: &Path) -> Result<()> {
let snapshot = format!("{}_skip_required_imports", path.to_string_lossy());
let diagnostics = test_path(
Path::new("pyupgrade").join(path).as_path(),
&settings::LinterSettings {
isort: isort::settings::Settings {
required_imports: BTreeSet::from_iter([
// https://github.com/astral-sh/ruff/issues/20601
NameImport::ImportFrom(MemberNameImport::member(
"builtins".to_string(),
"str".to_string(),
)),
]),
..Default::default()
},
..settings::LinterSettings::for_rules([
Rule::MissingRequiredImport,
Rule::UnnecessaryBuiltinImport,
])
},
)?;
assert_diagnostics!(snapshot, diagnostics);
Ok(())
}
#[test]
fn unnecessary_default_type_args_stubs_py312_preview() -> Result<()> {
let snapshot = format!("{}__preview", "UP043.pyi");

View file

@ -6,6 +6,7 @@ use ruff_text_size::Ranged;
use crate::checkers::ast::Checker;
use crate::fix;
use crate::rules::pyupgrade::rules::is_import_required_by_isort;
use crate::{AlwaysFixableViolation, Fix};
/// ## What it does
@ -85,6 +86,13 @@ pub(crate) fn unnecessary_builtin_import(
// Identify unaliased, builtin imports.
let unused_imports: Vec<&Alias> = names
.iter()
.filter(|alias| {
!is_import_required_by_isort(
&checker.settings().isort.required_imports,
stmt.into(),
alias,
)
})
.filter(|alias| alias.asname.is_none())
.filter(|alias| {
matches!(

View file

@ -2,7 +2,7 @@
source: crates/ruff_linter/src/rules/pyupgrade/mod.rs
---
UP029 [*] Unnecessary builtin import: `*`
--> UP029.py:1:1
--> UP029_0.py:1:1
|
1 | from builtins import *
| ^^^^^^^^^^^^^^^^^^^^^^
@ -17,7 +17,7 @@ help: Remove unnecessary builtin import
note: This is an unsafe fix and may change runtime behavior
UP029 [*] Unnecessary builtin imports: `ascii`, `bytes`
--> UP029.py:2:1
--> UP029_0.py:2:1
|
1 | from builtins import *
2 | from builtins import ascii, bytes, compile
@ -35,7 +35,7 @@ help: Remove unnecessary builtin import
note: This is an unsafe fix and may change runtime behavior
UP029 [*] Unnecessary builtin imports: `filter`, `zip`
--> UP029.py:4:1
--> UP029_0.py:4:1
|
2 | from builtins import ascii, bytes, compile
3 | from builtins import str as _str
@ -56,7 +56,7 @@ help: Remove unnecessary builtin import
note: This is an unsafe fix and may change runtime behavior
UP029 [*] Unnecessary builtin import: `open`
--> UP029.py:5:1
--> UP029_0.py:5:1
|
3 | from builtins import str as _str
4 | from six.moves import filter, zip, zip_longest

View file

@ -0,0 +1,13 @@
---
source: crates/ruff_linter/src/rules/pyupgrade/mod.rs
---
UP029 [*] Unnecessary builtin import: `int`
--> UP029_1.py:1:1
|
1 | from builtins import str, int
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
help: Remove unnecessary builtin import
- from builtins import str, int
1 + from builtins import str
note: This is an unsafe fix and may change runtime behavior