/
relative_imports.rs
136 lines (125 loc) · 3.99 KB
/
relative_imports.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
use ruff_python_ast::{self as ast, Identifier, Stmt};
use ruff_text_size::{Ranged, TextRange};
use ruff_diagnostics::{Diagnostic, Edit, Fix, FixAvailability, Violation};
use ruff_macros::{derive_message_formats, violation};
use ruff_python_ast::helpers::resolve_imported_module_path;
use ruff_python_codegen::Generator;
use ruff_python_stdlib::identifiers::is_identifier;
use crate::checkers::ast::Checker;
use crate::rules::flake8_tidy_imports::settings::Strictness;
/// ## What it does
/// Checks for relative imports.
///
/// ## Why is this bad?
/// Absolute imports, or relative imports from siblings, are recommended by [PEP 8]:
///
/// > Absolute imports are recommended, as they are usually more readable and tend to be better behaved...
/// > ```python
/// > import mypkg.sibling
/// > from mypkg import sibling
/// > from mypkg.sibling import example
/// > ```
/// > However, explicit relative imports are an acceptable alternative to absolute imports,
/// > especially when dealing with complex package layouts where using absolute imports would be
/// > unnecessarily verbose:
/// > ```python
/// > from . import sibling
/// > from .sibling import example
/// > ```
///
/// ## Example
/// ```python
/// from .. import foo
/// ```
///
/// Use instead:
/// ```python
/// from mypkg import foo
/// ```
///
/// ## Options
/// - `lint.flake8-tidy-imports.ban-relative-imports`
///
/// [PEP 8]: https://peps.python.org/pep-0008/#imports
#[violation]
pub struct RelativeImports {
strictness: Strictness,
}
impl Violation for RelativeImports {
const FIX_AVAILABILITY: FixAvailability = FixAvailability::Sometimes;
#[derive_message_formats]
fn message(&self) -> String {
match self.strictness {
Strictness::Parents => {
format!("Prefer absolute imports over relative imports from parent modules")
}
Strictness::All => format!("Prefer absolute imports over relative imports"),
}
}
fn fix_title(&self) -> Option<String> {
let RelativeImports { strictness } = self;
Some(match strictness {
Strictness::Parents => {
"Replace relative imports from parent modules with absolute imports".to_string()
}
Strictness::All => "Replace relative imports with absolute imports".to_string(),
})
}
}
fn fix_banned_relative_import(
stmt: &Stmt,
level: u32,
module: Option<&str>,
module_path: Option<&[String]>,
generator: Generator,
) -> Option<Fix> {
// Only fix is the module path is known.
let module_path = resolve_imported_module_path(level, module, module_path)?;
// Require import to be a valid module:
// https://python.org/dev/peps/pep-0008/#package-and-module-names
if !module_path.split('.').all(is_identifier) {
return None;
}
let Stmt::ImportFrom(ast::StmtImportFrom { names, .. }) = stmt else {
panic!("Expected Stmt::ImportFrom");
};
let node = ast::StmtImportFrom {
module: Some(Identifier::new(
module_path.to_string(),
TextRange::default(),
)),
names: names.clone(),
level: 0,
range: TextRange::default(),
};
let content = generator.stmt(&node.into());
Some(Fix::unsafe_edit(Edit::range_replacement(
content,
stmt.range(),
)))
}
/// TID252
pub(crate) fn banned_relative_import(
checker: &Checker,
stmt: &Stmt,
level: u32,
module: Option<&str>,
module_path: Option<&[String]>,
strictness: Strictness,
) -> Option<Diagnostic> {
let strictness_level = match strictness {
Strictness::All => 0,
Strictness::Parents => 1,
};
if level > strictness_level {
let mut diagnostic = Diagnostic::new(RelativeImports { strictness }, stmt.range());
if let Some(fix) =
fix_banned_relative_import(stmt, level, module, module_path, checker.generator())
{
diagnostic.set_fix(fix);
};
Some(diagnostic)
} else {
None
}
}