Remove qualified name
This commit is contained in:
@@ -26,7 +26,6 @@
|
||||
//! represents the lint-rule analysis phase. In the future, these steps may be separated into
|
||||
//! distinct passes over the AST.
|
||||
|
||||
use std::iter::once;
|
||||
use std::path::Path;
|
||||
|
||||
use itertools::Itertools;
|
||||
@@ -40,7 +39,7 @@ use ruff_text_size::{TextRange, TextSize};
|
||||
use ruff_diagnostics::{Diagnostic, IsolationLevel};
|
||||
use ruff_python_ast::all::{extract_all_names, DunderAllFlags};
|
||||
use ruff_python_ast::helpers::{
|
||||
extract_handled_exceptions, from_relative_import, from_relative_import_parts, to_module_path,
|
||||
extract_handled_exceptions, from_relative_import_parts, to_module_path,
|
||||
};
|
||||
use ruff_python_ast::identifier::Identifier;
|
||||
use ruff_python_ast::str::trailing_quote;
|
||||
@@ -322,15 +321,11 @@ where
|
||||
// Given `import foo.bar`, `name` would be "foo", and `qualified_name` would be
|
||||
// "foo.bar".
|
||||
let name = alias.name.split('.').next().unwrap();
|
||||
let qualified_name = &alias.name;
|
||||
let call_path: Box<[&str]> = qualified_name.split('.').collect();
|
||||
let call_path: Box<[&str]> = alias.name.split('.').collect();
|
||||
self.add_binding(
|
||||
name,
|
||||
alias.identifier(),
|
||||
BindingKind::SubmoduleImport(SubmoduleImport {
|
||||
qualified_name,
|
||||
call_path,
|
||||
}),
|
||||
BindingKind::SubmoduleImport(SubmoduleImport { call_path }),
|
||||
BindingFlags::EXTERNAL,
|
||||
);
|
||||
} else {
|
||||
@@ -347,15 +342,11 @@ where
|
||||
}
|
||||
|
||||
let name = alias.asname.as_ref().unwrap_or(&alias.name);
|
||||
let qualified_name = &alias.name;
|
||||
let call_path: Box<[&str]> = qualified_name.split('.').collect();
|
||||
let call_path: Box<[&str]> = alias.name.split('.').collect();
|
||||
self.add_binding(
|
||||
name,
|
||||
alias.identifier(),
|
||||
BindingKind::Import(Import {
|
||||
qualified_name,
|
||||
call_path,
|
||||
}),
|
||||
BindingKind::Import(Import { call_path }),
|
||||
flags,
|
||||
);
|
||||
}
|
||||
@@ -399,25 +390,20 @@ where
|
||||
// be "foo.bar". Given `from foo import bar as baz`, `name` would be "baz"
|
||||
// and `qualified_name` would be "foo.bar".
|
||||
let name = alias.asname.as_ref().unwrap_or(&alias.name);
|
||||
|
||||
let qualified_name =
|
||||
helpers::format_import_from_member(level, module, &alias.name);
|
||||
let call_path = from_relative_import_parts(
|
||||
if let Some(call_path) = from_relative_import_parts(
|
||||
self.module_path.unwrap_or_default(),
|
||||
level,
|
||||
module,
|
||||
&alias.name,
|
||||
);
|
||||
let call_path: Box<[&str]> = call_path.into_boxed_slice();
|
||||
self.add_binding(
|
||||
name,
|
||||
alias.identifier(),
|
||||
BindingKind::FromImport(FromImport {
|
||||
qualified_name,
|
||||
call_path,
|
||||
}),
|
||||
flags,
|
||||
);
|
||||
) {
|
||||
let call_path: Box<[&str]> = call_path.into_boxed_slice();
|
||||
self.add_binding(
|
||||
name,
|
||||
alias.identifier(),
|
||||
BindingKind::FromImport(FromImport { call_path }),
|
||||
flags,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -231,7 +231,7 @@ impl Renamer {
|
||||
}
|
||||
BindingKind::SubmoduleImport(import) => {
|
||||
// Ex) Rename `import pandas.core` to `import pandas as pd`.
|
||||
let module_name = import.qualified_name.split('.').next().unwrap();
|
||||
let module_name = import.call_path.first().unwrap();
|
||||
Some(Edit::range_replacement(
|
||||
format!("{module_name} as {target}"),
|
||||
binding.range,
|
||||
|
||||
@@ -60,7 +60,7 @@ pub(crate) fn unconventional_import_alias(
|
||||
return None;
|
||||
};
|
||||
|
||||
let Some(expected_alias) = conventions.get(qualified_name) else {
|
||||
let Some(expected_alias) = conventions.get(qualified_name.as_str()) else {
|
||||
return None;
|
||||
};
|
||||
|
||||
|
||||
@@ -50,10 +50,10 @@ pub(crate) fn unaliased_collections_abc_set_import(
|
||||
checker: &Checker,
|
||||
binding: &Binding,
|
||||
) -> Option<Diagnostic> {
|
||||
let BindingKind::FromImport(FromImport { qualified_name, .. }) = &binding.kind else {
|
||||
let BindingKind::FromImport(FromImport { call_path }) = &binding.kind else {
|
||||
return None;
|
||||
};
|
||||
if qualified_name.as_str() != "collections.abc.Set" {
|
||||
if !matches!(call_path.as_ref(), ["collections", "abc", "Set"]) {
|
||||
return None;
|
||||
}
|
||||
|
||||
|
||||
@@ -75,7 +75,7 @@ pub(crate) fn runtime_import_in_type_checking_block(
|
||||
for binding_id in scope.binding_ids() {
|
||||
let binding = checker.semantic().binding(binding_id);
|
||||
|
||||
let Some(qualified_name) = binding.qualified_name() else {
|
||||
let Some(call_path) = binding.call_path() else {
|
||||
continue;
|
||||
};
|
||||
|
||||
@@ -97,7 +97,7 @@ pub(crate) fn runtime_import_in_type_checking_block(
|
||||
};
|
||||
|
||||
let import = Import {
|
||||
qualified_name,
|
||||
call_path,
|
||||
reference_id,
|
||||
range: binding.range,
|
||||
parent_range: binding.parent_range(checker.semantic()),
|
||||
@@ -131,7 +131,7 @@ pub(crate) fn runtime_import_in_type_checking_block(
|
||||
};
|
||||
|
||||
for Import {
|
||||
qualified_name,
|
||||
call_path,
|
||||
range,
|
||||
parent_range,
|
||||
..
|
||||
@@ -139,7 +139,7 @@ pub(crate) fn runtime_import_in_type_checking_block(
|
||||
{
|
||||
let mut diagnostic = Diagnostic::new(
|
||||
RuntimeImportInTypeCheckingBlock {
|
||||
qualified_name: qualified_name.to_string(),
|
||||
qualified_name: call_path.join("."),
|
||||
},
|
||||
range,
|
||||
);
|
||||
@@ -156,7 +156,7 @@ pub(crate) fn runtime_import_in_type_checking_block(
|
||||
// Separately, generate a diagnostic for every _ignored_ import, to ensure that the
|
||||
// suppression comments aren't marked as unused.
|
||||
for Import {
|
||||
qualified_name,
|
||||
call_path,
|
||||
range,
|
||||
parent_range,
|
||||
..
|
||||
@@ -164,7 +164,7 @@ pub(crate) fn runtime_import_in_type_checking_block(
|
||||
{
|
||||
let mut diagnostic = Diagnostic::new(
|
||||
RuntimeImportInTypeCheckingBlock {
|
||||
qualified_name: qualified_name.to_string(),
|
||||
qualified_name: call_path.join("."),
|
||||
},
|
||||
range,
|
||||
);
|
||||
@@ -178,7 +178,7 @@ pub(crate) fn runtime_import_in_type_checking_block(
|
||||
/// A runtime-required import with its surrounding context.
|
||||
struct Import<'a> {
|
||||
/// The qualified name of the import (e.g., `typing.List` for `from typing import List`).
|
||||
qualified_name: &'a str,
|
||||
call_path: &'a [&'a str],
|
||||
/// The first reference to the imported symbol.
|
||||
reference_id: ResolvedReferenceId,
|
||||
/// The trimmed range of the import (e.g., `List` in `from typing import List`).
|
||||
@@ -191,9 +191,9 @@ struct Import<'a> {
|
||||
fn fix_imports(checker: &Checker, stmt_id: NodeId, imports: &[Import]) -> Result<Fix> {
|
||||
let stmt = checker.semantic().stmts[stmt_id];
|
||||
let parent = checker.semantic().stmts.parent(stmt);
|
||||
let qualified_names: Vec<&str> = imports
|
||||
let qualified_names: Vec<String> = imports
|
||||
.iter()
|
||||
.map(|Import { qualified_name, .. }| *qualified_name)
|
||||
.map(|Import { call_path, .. }| call_path.join("."))
|
||||
.collect();
|
||||
|
||||
// Find the first reference across all imports.
|
||||
@@ -207,7 +207,7 @@ fn fix_imports(checker: &Checker, stmt_id: NodeId, imports: &[Import]) -> Result
|
||||
|
||||
// Step 1) Remove the import.
|
||||
let remove_import_edit = autofix::edits::remove_unused_imports(
|
||||
qualified_names.iter().copied(),
|
||||
qualified_names.iter().map(|name| name.as_str()),
|
||||
stmt,
|
||||
parent,
|
||||
checker.locator(),
|
||||
@@ -219,7 +219,7 @@ fn fix_imports(checker: &Checker, stmt_id: NodeId, imports: &[Import]) -> Result
|
||||
let add_import_edit = checker.importer().runtime_import_edit(
|
||||
&StmtImports {
|
||||
stmt,
|
||||
qualified_names,
|
||||
qualified_names: qualified_names.iter().map(|name| name.as_str()).collect(),
|
||||
},
|
||||
at,
|
||||
)?;
|
||||
|
||||
@@ -211,7 +211,7 @@ pub(crate) fn typing_only_runtime_import(
|
||||
};
|
||||
|
||||
if is_exempt(
|
||||
qualified_name,
|
||||
qualified_name.as_str(),
|
||||
&checker
|
||||
.settings
|
||||
.flake8_type_checking
|
||||
@@ -248,7 +248,7 @@ pub(crate) fn typing_only_runtime_import(
|
||||
|
||||
// Categorize the import, using coarse-grained categorization.
|
||||
let import_type = match categorize(
|
||||
qualified_name,
|
||||
qualified_name.as_str(),
|
||||
Some(level),
|
||||
&checker.settings.src,
|
||||
checker.package(),
|
||||
@@ -353,9 +353,9 @@ pub(crate) fn typing_only_runtime_import(
|
||||
}
|
||||
|
||||
/// A runtime-required import with its surrounding context.
|
||||
struct Import<'a> {
|
||||
struct Import {
|
||||
/// The qualified name of the import (e.g., `typing.List` for `from typing import List`).
|
||||
qualified_name: &'a str,
|
||||
qualified_name: String,
|
||||
/// The first reference to the imported symbol.
|
||||
reference_id: ResolvedReferenceId,
|
||||
/// The trimmed range of the import (e.g., `List` in `from typing import List`).
|
||||
@@ -417,7 +417,7 @@ fn fix_imports(checker: &Checker, stmt_id: NodeId, imports: &[Import]) -> Result
|
||||
let parent = checker.semantic().stmts.parent(stmt);
|
||||
let qualified_names: Vec<&str> = imports
|
||||
.iter()
|
||||
.map(|Import { qualified_name, .. }| *qualified_name)
|
||||
.map(|Import { qualified_name, .. }| qualified_name.as_str())
|
||||
.collect();
|
||||
|
||||
// Find the first reference across all imports.
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
source: crates/ruff/src/rules/flake8_type_checking/mod.rs
|
||||
assertion_line: 43
|
||||
---
|
||||
|
||||
@@ -26,28 +26,25 @@ pub(super) fn test_expression(expr: &Expr, semantic: &SemanticModel) -> Resoluti
|
||||
| Expr::ListComp(_)
|
||||
| Expr::DictComp(_)
|
||||
| Expr::GeneratorExp(_) => Resolution::IrrelevantExpression,
|
||||
Expr::Name(ast::ExprName { id, .. }) => {
|
||||
semantic
|
||||
.find_binding(id)
|
||||
.map_or(Resolution::IrrelevantBinding, |binding| {
|
||||
match binding.kind {
|
||||
BindingKind::Annotation
|
||||
| BindingKind::Argument
|
||||
| BindingKind::Assignment
|
||||
| BindingKind::NamedExprAssignment
|
||||
| BindingKind::UnpackedAssignment
|
||||
| BindingKind::LoopVar
|
||||
| BindingKind::Global
|
||||
| BindingKind::Nonlocal(_) => Resolution::RelevantLocal,
|
||||
BindingKind::Import(Import { qualified_name, .. })
|
||||
if qualified_name == "pandas" =>
|
||||
{
|
||||
Resolution::PandasModule
|
||||
}
|
||||
_ => Resolution::IrrelevantBinding,
|
||||
}
|
||||
})
|
||||
}
|
||||
Expr::Name(ast::ExprName { id, .. }) => semantic.find_binding(id).map_or(
|
||||
Resolution::IrrelevantBinding,
|
||||
|binding| match &binding.kind {
|
||||
BindingKind::Annotation
|
||||
| BindingKind::Argument
|
||||
| BindingKind::Assignment
|
||||
| BindingKind::NamedExprAssignment
|
||||
| BindingKind::UnpackedAssignment
|
||||
| BindingKind::LoopVar
|
||||
| BindingKind::Global
|
||||
| BindingKind::Nonlocal(_) => Resolution::RelevantLocal,
|
||||
BindingKind::Import(Import { call_path })
|
||||
if matches!(call_path.as_ref(), ["pandas"]) =>
|
||||
{
|
||||
Resolution::PandasModule
|
||||
}
|
||||
_ => Resolution::IrrelevantBinding,
|
||||
},
|
||||
),
|
||||
_ => Resolution::RelevantLocal,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -65,13 +65,11 @@ pub(crate) fn inplace_argument(
|
||||
.first()
|
||||
.and_then(|module| checker.semantic().find_binding(module))
|
||||
.map_or(false, |binding| {
|
||||
matches!(
|
||||
binding.kind,
|
||||
BindingKind::Import(Import {
|
||||
qualified_name: "pandas",
|
||||
..
|
||||
})
|
||||
)
|
||||
if let BindingKind::Import(Import { call_path, .. }) = &binding.kind {
|
||||
matches!(call_path.as_ref(), ["pandas"])
|
||||
} else {
|
||||
false
|
||||
}
|
||||
})
|
||||
{
|
||||
return;
|
||||
|
||||
@@ -214,9 +214,9 @@ pub(crate) fn unused_import(checker: &Checker, scope: &Scope, diagnostics: &mut
|
||||
}
|
||||
|
||||
/// An unused import with its surrounding context.
|
||||
struct Import<'a> {
|
||||
struct Import {
|
||||
/// The qualified name of the import (e.g., `typing.List` for `from typing import List`).
|
||||
qualified_name: &'a str,
|
||||
qualified_name: String,
|
||||
/// The trimmed range of the import (e.g., `List` in `from typing import List`).
|
||||
range: TextRange,
|
||||
/// The range of the import's parent statement.
|
||||
@@ -230,7 +230,7 @@ fn fix_imports(checker: &Checker, stmt_id: NodeId, imports: &[Import]) -> Result
|
||||
let edit = autofix::edits::remove_unused_imports(
|
||||
imports
|
||||
.iter()
|
||||
.map(|Import { qualified_name, .. }| *qualified_name),
|
||||
.map(|Import { qualified_name, .. }| qualified_name.as_str()),
|
||||
stmt,
|
||||
parent,
|
||||
checker.locator(),
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
---
|
||||
source: crates/ruff/src/rules/pyflakes/mod.rs
|
||||
assertion_line: 147
|
||||
---
|
||||
F401_6.py:7:25: F401 [*] `pyflakes.background.BackgroundTasks` imported but unused
|
||||
|
|
||||
6 | # F401 `background.BackgroundTasks` imported but unused
|
||||
7 | from .background import BackgroundTasks
|
||||
| ^^^^^^^^^^^^^^^ F401
|
||||
8 |
|
||||
9 | # F401 `datastructures.UploadFile` imported but unused
|
||||
|
|
||||
= help: Remove unused import: `pyflakes.background.BackgroundTasks`
|
||||
|
||||
ℹ Fix
|
||||
|
||||
F401_6.py:10:43: F401 [*] `pyflakes.datastructures.UploadFile` imported but unused
|
||||
|
|
||||
9 | # F401 `datastructures.UploadFile` imported but unused
|
||||
10 | from .datastructures import UploadFile as FileUpload
|
||||
| ^^^^^^^^^^ F401
|
||||
11 |
|
||||
12 | # OK
|
||||
|
|
||||
= help: Remove unused import: `pyflakes.datastructures.UploadFile`
|
||||
|
||||
ℹ Fix
|
||||
|
||||
F401_6.py:16:8: F401 [*] `background` imported but unused
|
||||
|
|
||||
15 | # F401 `background` imported but unused
|
||||
16 | import background
|
||||
| ^^^^^^^^^^ F401
|
||||
17 |
|
||||
18 | # F401 `datastructures` imported but unused
|
||||
|
|
||||
= help: Remove unused import: `background`
|
||||
|
||||
ℹ Fix
|
||||
13 13 | import applications as applications
|
||||
14 14 |
|
||||
15 15 | # F401 `background` imported but unused
|
||||
16 |-import background
|
||||
17 16 |
|
||||
18 17 | # F401 `datastructures` imported but unused
|
||||
19 18 | import datastructures as structures
|
||||
|
||||
F401_6.py:19:26: F401 [*] `datastructures` imported but unused
|
||||
|
|
||||
18 | # F401 `datastructures` imported but unused
|
||||
19 | import datastructures as structures
|
||||
| ^^^^^^^^^^ F401
|
||||
|
|
||||
= help: Remove unused import: `datastructures`
|
||||
|
||||
ℹ Fix
|
||||
16 16 | import background
|
||||
17 17 |
|
||||
18 18 | # F401 `datastructures` imported but unused
|
||||
19 |-import datastructures as structures
|
||||
|
||||
|
||||
@@ -0,0 +1,268 @@
|
||||
---
|
||||
source: crates/ruff/src/rules/pyupgrade/mod.rs
|
||||
assertion_line: 88
|
||||
---
|
||||
UP024_0.py:6:8: UP024 [*] Replace aliased errors with `OSError`
|
||||
|
|
||||
4 | try:
|
||||
5 | pass
|
||||
6 | except EnvironmentError:
|
||||
| ^^^^^^^^^^^^^^^^ UP024
|
||||
7 | pass
|
||||
|
|
||||
= help: Replace `EnvironmentError` with builtin `OSError`
|
||||
|
||||
ℹ Fix
|
||||
3 3 | # These should be fixed
|
||||
4 4 | try:
|
||||
5 5 | pass
|
||||
6 |-except EnvironmentError:
|
||||
6 |+except OSError:
|
||||
7 7 | pass
|
||||
8 8 |
|
||||
9 9 | try:
|
||||
|
||||
UP024_0.py:11:8: UP024 [*] Replace aliased errors with `OSError`
|
||||
|
|
||||
9 | try:
|
||||
10 | pass
|
||||
11 | except IOError:
|
||||
| ^^^^^^^ UP024
|
||||
12 | pass
|
||||
|
|
||||
= help: Replace `IOError` with builtin `OSError`
|
||||
|
||||
ℹ Fix
|
||||
8 8 |
|
||||
9 9 | try:
|
||||
10 10 | pass
|
||||
11 |-except IOError:
|
||||
11 |+except OSError:
|
||||
12 12 | pass
|
||||
13 13 |
|
||||
14 14 | try:
|
||||
|
||||
UP024_0.py:16:8: UP024 [*] Replace aliased errors with `OSError`
|
||||
|
|
||||
14 | try:
|
||||
15 | pass
|
||||
16 | except WindowsError:
|
||||
| ^^^^^^^^^^^^ UP024
|
||||
17 | pass
|
||||
|
|
||||
= help: Replace `WindowsError` with builtin `OSError`
|
||||
|
||||
ℹ Fix
|
||||
13 13 |
|
||||
14 14 | try:
|
||||
15 15 | pass
|
||||
16 |-except WindowsError:
|
||||
16 |+except OSError:
|
||||
17 17 | pass
|
||||
18 18 |
|
||||
19 19 | try:
|
||||
|
||||
UP024_0.py:21:8: UP024 [*] Replace aliased errors with `OSError`
|
||||
|
|
||||
19 | try:
|
||||
20 | pass
|
||||
21 | except mmap.error:
|
||||
| ^^^^^^^^^^ UP024
|
||||
22 | pass
|
||||
|
|
||||
= help: Replace `mmap.error` with builtin `OSError`
|
||||
|
||||
ℹ Fix
|
||||
18 18 |
|
||||
19 19 | try:
|
||||
20 20 | pass
|
||||
21 |-except mmap.error:
|
||||
21 |+except OSError:
|
||||
22 22 | pass
|
||||
23 23 |
|
||||
24 24 | try:
|
||||
|
||||
UP024_0.py:26:8: UP024 [*] Replace aliased errors with `OSError`
|
||||
|
|
||||
24 | try:
|
||||
25 | pass
|
||||
26 | except select.error:
|
||||
| ^^^^^^^^^^^^ UP024
|
||||
27 | pass
|
||||
|
|
||||
= help: Replace `select.error` with builtin `OSError`
|
||||
|
||||
ℹ Fix
|
||||
23 23 |
|
||||
24 24 | try:
|
||||
25 25 | pass
|
||||
26 |-except select.error:
|
||||
26 |+except OSError:
|
||||
27 27 | pass
|
||||
28 28 |
|
||||
29 29 | try:
|
||||
|
||||
UP024_0.py:31:8: UP024 [*] Replace aliased errors with `OSError`
|
||||
|
|
||||
29 | try:
|
||||
30 | pass
|
||||
31 | except socket.error:
|
||||
| ^^^^^^^^^^^^ UP024
|
||||
32 | pass
|
||||
|
|
||||
= help: Replace `socket.error` with builtin `OSError`
|
||||
|
||||
ℹ Fix
|
||||
28 28 |
|
||||
29 29 | try:
|
||||
30 30 | pass
|
||||
31 |-except socket.error:
|
||||
31 |+except OSError:
|
||||
32 32 | pass
|
||||
33 33 |
|
||||
34 34 | try:
|
||||
|
||||
UP024_0.py:36:8: UP024 [*] Replace aliased errors with `OSError`
|
||||
|
|
||||
34 | try:
|
||||
35 | pass
|
||||
36 | except error:
|
||||
| ^^^^^ UP024
|
||||
37 | pass
|
||||
|
|
||||
= help: Replace `error` with builtin `OSError`
|
||||
|
||||
ℹ Fix
|
||||
33 33 |
|
||||
34 34 | try:
|
||||
35 35 | pass
|
||||
36 |-except error:
|
||||
36 |+except OSError:
|
||||
37 37 | pass
|
||||
38 38 |
|
||||
39 39 | # Should NOT be in parentheses when replaced
|
||||
|
||||
UP024_0.py:43:8: UP024 [*] Replace aliased errors with `OSError`
|
||||
|
|
||||
41 | try:
|
||||
42 | pass
|
||||
43 | except (IOError,):
|
||||
| ^^^^^^^^^^ UP024
|
||||
44 | pass
|
||||
45 | try:
|
||||
|
|
||||
= help: Replace with builtin `OSError`
|
||||
|
||||
ℹ Fix
|
||||
40 40 |
|
||||
41 41 | try:
|
||||
42 42 | pass
|
||||
43 |-except (IOError,):
|
||||
43 |+except OSError:
|
||||
44 44 | pass
|
||||
45 45 | try:
|
||||
46 46 | pass
|
||||
|
||||
UP024_0.py:47:8: UP024 [*] Replace aliased errors with `OSError`
|
||||
|
|
||||
45 | try:
|
||||
46 | pass
|
||||
47 | except (mmap.error,):
|
||||
| ^^^^^^^^^^^^^ UP024
|
||||
48 | pass
|
||||
49 | try:
|
||||
|
|
||||
= help: Replace with builtin `OSError`
|
||||
|
||||
ℹ Fix
|
||||
44 44 | pass
|
||||
45 45 | try:
|
||||
46 46 | pass
|
||||
47 |-except (mmap.error,):
|
||||
47 |+except OSError:
|
||||
48 48 | pass
|
||||
49 49 | try:
|
||||
50 50 | pass
|
||||
|
||||
UP024_0.py:51:8: UP024 [*] Replace aliased errors with `OSError`
|
||||
|
|
||||
49 | try:
|
||||
50 | pass
|
||||
51 | except (EnvironmentError, IOError, OSError, select.error):
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ UP024
|
||||
52 | pass
|
||||
|
|
||||
= help: Replace with builtin `OSError`
|
||||
|
||||
ℹ Fix
|
||||
48 48 | pass
|
||||
49 49 | try:
|
||||
50 50 | pass
|
||||
51 |-except (EnvironmentError, IOError, OSError, select.error):
|
||||
51 |+except OSError:
|
||||
52 52 | pass
|
||||
53 53 |
|
||||
54 54 | # Should be kept in parentheses (because multiple)
|
||||
|
||||
UP024_0.py:58:8: UP024 [*] Replace aliased errors with `OSError`
|
||||
|
|
||||
56 | try:
|
||||
57 | pass
|
||||
58 | except (IOError, KeyError, OSError):
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ UP024
|
||||
59 | pass
|
||||
|
|
||||
= help: Replace with builtin `OSError`
|
||||
|
||||
ℹ Fix
|
||||
55 55 |
|
||||
56 56 | try:
|
||||
57 57 | pass
|
||||
58 |-except (IOError, KeyError, OSError):
|
||||
58 |+except (KeyError, OSError):
|
||||
59 59 | pass
|
||||
60 60 |
|
||||
61 61 | # First should change, second should not
|
||||
|
||||
UP024_0.py:65:8: UP024 [*] Replace aliased errors with `OSError`
|
||||
|
|
||||
63 | try:
|
||||
64 | pass
|
||||
65 | except (IOError, error):
|
||||
| ^^^^^^^^^^^^^^^^ UP024
|
||||
66 | pass
|
||||
67 | # These should not change
|
||||
|
|
||||
= help: Replace with builtin `OSError`
|
||||
|
||||
ℹ Fix
|
||||
62 62 | from .mmap import error
|
||||
63 63 | try:
|
||||
64 64 | pass
|
||||
65 |-except (IOError, error):
|
||||
65 |+except OSError:
|
||||
66 66 | pass
|
||||
67 67 | # These should not change
|
||||
68 68 |
|
||||
|
||||
UP024_0.py:87:8: UP024 [*] Replace aliased errors with `OSError`
|
||||
|
|
||||
85 | try:
|
||||
86 | pass
|
||||
87 | except (mmap).error:
|
||||
| ^^^^^^^^^^^^ UP024
|
||||
88 | pass
|
||||
|
|
||||
= help: Replace `mmap.error` with builtin `OSError`
|
||||
|
||||
ℹ Fix
|
||||
84 84 | pass
|
||||
85 85 | try:
|
||||
86 86 | pass
|
||||
87 |-except (mmap).error:
|
||||
87 |+except OSError:
|
||||
88 88 | pass
|
||||
89 89 |
|
||||
90 90 | try:
|
||||
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
use crate::{nodes, Expr};
|
||||
use smallvec::{smallvec, SmallVec};
|
||||
|
||||
use crate::{nodes, Expr};
|
||||
|
||||
/// A representation of a qualified name, like `typing.List`.
|
||||
pub type CallPath<'a> = SmallVec<[&'a str; 8]>;
|
||||
|
||||
/// Convert an `Expr` to its [`CallPath`] segments (like `["typing", "List"]`).
|
||||
pub fn collect_head_path(expr: &Expr) -> Option<(&ast::ExprName, CallPath)> {
|
||||
pub fn collect_head_path(expr: &Expr) -> Option<(&nodes::ExprName, CallPath)> {
|
||||
// Unroll the loop up to eight times, to match the maximum number of expected attributes.
|
||||
// In practice, unrolling appears to give about a 4x speed-up on this hot path.
|
||||
let attr1 = match expr {
|
||||
|
||||
@@ -899,18 +899,21 @@ pub fn from_relative_import_parts<'a>(
|
||||
level: Option<u32>,
|
||||
module: Option<&'a str>,
|
||||
member: &'a str,
|
||||
) -> CallPath<'a> {
|
||||
) -> Option<CallPath<'a>> {
|
||||
let mut call_path: CallPath = SmallVec::with_capacity(module_path.len() + 1);
|
||||
|
||||
// Start with the module path.
|
||||
call_path.extend(module_path.iter().map(String::as_str));
|
||||
|
||||
// Remove segments based on the number of dots.
|
||||
for _ in 0..level.unwrap_or(0) {
|
||||
if call_path.is_empty() {
|
||||
return SmallVec::new();
|
||||
if let Some(level) = level {
|
||||
if level > 0 {
|
||||
call_path.extend(module_path.iter().map(String::as_str));
|
||||
|
||||
for _ in 0..level {
|
||||
if call_path.is_empty() {
|
||||
return None;
|
||||
}
|
||||
call_path.pop();
|
||||
}
|
||||
}
|
||||
call_path.pop();
|
||||
}
|
||||
|
||||
// Add the remaining segments.
|
||||
@@ -921,7 +924,7 @@ pub fn from_relative_import_parts<'a>(
|
||||
// Add the member.
|
||||
call_path.push(member);
|
||||
|
||||
call_path
|
||||
Some(call_path)
|
||||
}
|
||||
|
||||
/// Given an imported module (based on its relative import level and module name), return the
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
use std::ops::{Deref, DerefMut};
|
||||
|
||||
use bitflags::bitflags;
|
||||
use ruff_python_ast::Ranged;
|
||||
use ruff_text_size::TextRange;
|
||||
|
||||
use ruff_index::{newtype_index, IndexSlice, IndexVec};
|
||||
use ruff_python_ast::source_code::Locator;
|
||||
use ruff_python_ast::call_path::format_call_path;
|
||||
use ruff_python_ast::Ranged;
|
||||
use ruff_source_file::Locator;
|
||||
use ruff_text_size::TextRange;
|
||||
|
||||
use crate::context::ExecutionContext;
|
||||
use crate::model::SemanticModel;
|
||||
@@ -177,30 +177,29 @@ impl<'a> Binding<'a> {
|
||||
}
|
||||
|
||||
/// Returns the fully-qualified symbol name, if this symbol was imported from another module.
|
||||
pub fn qualified_name(&self) -> Option<&str> {
|
||||
pub fn call_path(&self) -> Option<&[&str]> {
|
||||
match &self.kind {
|
||||
BindingKind::Import(Import { qualified_name, .. }) => Some(qualified_name),
|
||||
BindingKind::FromImport(FromImport { qualified_name, .. }) => Some(qualified_name),
|
||||
BindingKind::SubmoduleImport(SubmoduleImport { qualified_name, .. }) => {
|
||||
Some(qualified_name)
|
||||
}
|
||||
BindingKind::Import(Import { call_path }) => Some(call_path),
|
||||
BindingKind::FromImport(FromImport { call_path }) => Some(call_path),
|
||||
BindingKind::SubmoduleImport(SubmoduleImport { call_path }) => Some(call_path),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the fully-qualified symbol name, if this symbol was imported from another module.
|
||||
pub fn qualified_name(&self) -> Option<String> {
|
||||
self.call_path().map(format_call_path)
|
||||
}
|
||||
|
||||
/// Returns the fully-qualified name of the module from which this symbol was imported, if this
|
||||
/// symbol was imported from another module.
|
||||
pub fn module_name(&self) -> Option<&str> {
|
||||
pub fn module_name(&self) -> Option<&[&str]> {
|
||||
match &self.kind {
|
||||
BindingKind::Import(Import { qualified_name, .. })
|
||||
| BindingKind::SubmoduleImport(SubmoduleImport { qualified_name, .. }) => {
|
||||
Some(qualified_name.split('.').next().unwrap_or(qualified_name))
|
||||
BindingKind::Import(Import { call_path })
|
||||
| BindingKind::SubmoduleImport(SubmoduleImport { call_path }) => Some(&call_path[..1]),
|
||||
BindingKind::FromImport(FromImport { call_path }) => {
|
||||
Some(&call_path[..call_path.len() - 1])
|
||||
}
|
||||
BindingKind::FromImport(FromImport { qualified_name, .. }) => Some(
|
||||
qualified_name
|
||||
.rsplit_once('.')
|
||||
.map_or(qualified_name, |(module, _)| module),
|
||||
),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
@@ -357,7 +356,6 @@ pub struct Import<'a> {
|
||||
/// The full name of the module being imported.
|
||||
/// Ex) Given `import foo`, `qualified_name` would be "foo".
|
||||
/// Ex) Given `import foo as bar`, `qualified_name` would be "foo".
|
||||
pub qualified_name: &'a str,
|
||||
pub call_path: Box<[&'a str]>,
|
||||
}
|
||||
|
||||
@@ -369,7 +367,6 @@ pub struct FromImport<'a> {
|
||||
/// The full name of the member being imported.
|
||||
/// Ex) Given `from foo import bar`, `qualified_name` would be "foo.bar".
|
||||
/// Ex) Given `from foo import bar as baz`, `qualified_name` would be "foo.bar".
|
||||
pub qualified_name: String,
|
||||
pub call_path: Box<[&'a str]>,
|
||||
}
|
||||
|
||||
@@ -379,7 +376,6 @@ pub struct FromImport<'a> {
|
||||
pub struct SubmoduleImport<'a> {
|
||||
/// The full name of the submodule being imported.
|
||||
/// Ex) Given `import foo.bar`, `qualified_name` would be "foo.bar".
|
||||
pub qualified_name: &'a str,
|
||||
pub call_path: Box<[&'a str]>,
|
||||
}
|
||||
|
||||
|
||||
@@ -29,7 +29,6 @@ use crate::{UnresolvedReference, UnresolvedReferenceFlags};
|
||||
/// A semantic model for a Python module, to enable querying the module's semantic information.
|
||||
pub struct SemanticModel<'a> {
|
||||
typing_modules: &'a [String],
|
||||
module_path: Option<&'a [String]>,
|
||||
|
||||
/// Stack of all visited statements.
|
||||
pub stmts: Nodes<'a>,
|
||||
@@ -129,7 +128,6 @@ impl<'a> SemanticModel<'a> {
|
||||
pub fn new(typing_modules: &'a [String], path: &'a Path, module: Module<'a>) -> Self {
|
||||
Self {
|
||||
typing_modules,
|
||||
module_path: module.path(),
|
||||
stmts: Nodes::default(),
|
||||
stmt_id: None,
|
||||
exprs: Vec::default(),
|
||||
@@ -585,17 +583,13 @@ impl<'a> SemanticModel<'a> {
|
||||
// import pyarrow.csv
|
||||
// print(pa.csv.read_csv("test.csv"))
|
||||
// ```
|
||||
let qualified_name = self.bindings[binding_id].qualified_name()?;
|
||||
let has_alias = qualified_name
|
||||
.split('.')
|
||||
.last()
|
||||
.map(|segment| segment != symbol)
|
||||
.unwrap_or_default();
|
||||
if !has_alias {
|
||||
let call_path = self.bindings[binding_id].call_path()?;
|
||||
let segment = call_path.last()?;
|
||||
if *segment == symbol {
|
||||
return None;
|
||||
}
|
||||
|
||||
let binding_id = self.scopes[scope_id].get(qualified_name)?;
|
||||
let binding_id = self.scopes[scope_id].get(segment)?;
|
||||
if !self.bindings[binding_id].kind.is_submodule_import() {
|
||||
return None;
|
||||
}
|
||||
@@ -635,23 +629,17 @@ impl<'a> SemanticModel<'a> {
|
||||
};
|
||||
|
||||
match &binding.kind {
|
||||
BindingKind::Import(Import { call_path, .. }) => {
|
||||
BindingKind::Import(Import { call_path }) => {
|
||||
let resolved: CallPath = call_path.iter().chain(tail.iter()).copied().collect();
|
||||
// resolved.extend_from_slice(call_path);
|
||||
// resolved.extend_from_slice(tail);
|
||||
Some(resolved)
|
||||
}
|
||||
BindingKind::SubmoduleImport(SubmoduleImport { qualified_name, .. }) => {
|
||||
let name = qualified_name.split('.').next().unwrap_or(qualified_name);
|
||||
let mut source_path: CallPath = from_unqualified_name(name);
|
||||
BindingKind::SubmoduleImport(SubmoduleImport { call_path }) => {
|
||||
let mut source_path: CallPath = CallPath::from_slice(&call_path[..1]);
|
||||
source_path.extend_from_slice(tail.as_slice());
|
||||
Some(source_path)
|
||||
}
|
||||
BindingKind::FromImport(FromImport { call_path, .. }) => {
|
||||
BindingKind::FromImport(FromImport { call_path }) => {
|
||||
let resolved: CallPath = call_path.iter().chain(tail.iter()).copied().collect();
|
||||
// let mut resolved = SmallVec::with_capacity(call_path.len() + tail.len());
|
||||
// resolved.extend_from_slice(call_path);
|
||||
// resolved.extend_from_slice(tail);
|
||||
Some(resolved)
|
||||
}
|
||||
BindingKind::Builtin => Some(smallvec!["", head.id.as_str()]),
|
||||
@@ -676,6 +664,7 @@ impl<'a> SemanticModel<'a> {
|
||||
module: &str,
|
||||
member: &str,
|
||||
) -> Option<ImportedName> {
|
||||
let module_path: Vec<&str> = module.split('.').collect();
|
||||
self.scopes().enumerate().find_map(|(scope_index, scope)| {
|
||||
scope.bindings().find_map(|(name, binding_id)| {
|
||||
let binding = &self.bindings[binding_id];
|
||||
@@ -683,8 +672,8 @@ impl<'a> SemanticModel<'a> {
|
||||
// Ex) Given `module="sys"` and `object="exit"`:
|
||||
// `import sys` -> `sys.exit`
|
||||
// `import sys as sys2` -> `sys2.exit`
|
||||
BindingKind::Import(Import { qualified_name, .. }) => {
|
||||
if qualified_name == &module {
|
||||
BindingKind::Import(Import { call_path }) => {
|
||||
if call_path.as_ref() == module_path.as_slice() {
|
||||
if let Some(source) = binding.source {
|
||||
// Verify that `sys` isn't bound in an inner scope.
|
||||
if self
|
||||
@@ -704,10 +693,9 @@ impl<'a> SemanticModel<'a> {
|
||||
// Ex) Given `module="os.path"` and `object="join"`:
|
||||
// `from os.path import join` -> `join`
|
||||
// `from os.path import join as join2` -> `join2`
|
||||
BindingKind::FromImport(FromImport { qualified_name, .. }) => {
|
||||
if let Some((target_module, target_member)) = qualified_name.split_once('.')
|
||||
{
|
||||
if target_module == module && target_member == member {
|
||||
BindingKind::FromImport(FromImport { call_path }) => {
|
||||
if let Some((target_member, target_module)) = call_path.split_last() {
|
||||
if target_module == module_path.as_slice() && target_member == &member {
|
||||
if let Some(source) = binding.source {
|
||||
// Verify that `join` isn't bound in an inner scope.
|
||||
if self
|
||||
|
||||
Reference in New Issue
Block a user