mirror of
https://github.com/astral-sh/ruff.git
synced 2025-09-26 20:10:09 +00:00
Implement autofix for relative imports (TID252) (#2739)
This commit is contained in:
parent
dadbfea497
commit
e83ed0ecba
8 changed files with 619 additions and 83 deletions
|
@ -1249,7 +1249,7 @@ For more, see [flake8-tidy-imports](https://pypi.org/project/flake8-tidy-imports
|
||||||
| Code | Name | Message | Fix |
|
| Code | Name | Message | Fix |
|
||||||
| ---- | ---- | ------- | --- |
|
| ---- | ---- | ------- | --- |
|
||||||
| TID251 | banned-api | `{name}` is banned: {message} | |
|
| TID251 | banned-api | `{name}` is banned: {message} | |
|
||||||
| TID252 | relative-imports | Relative imports from parent modules are banned | |
|
| TID252 | [relative-imports](https://github.com/charliermarsh/ruff/blob/main/docs/rules/relative-imports.md) | Relative imports from parent modules are banned | 🛠 |
|
||||||
|
|
||||||
### flake8-type-checking (TCH)
|
### flake8-type-checking (TCH)
|
||||||
|
|
||||||
|
|
|
@ -1,12 +1,30 @@
|
||||||
from . import sibling
|
# OK
|
||||||
from .sibling import example
|
|
||||||
|
|
||||||
from .. import parent
|
|
||||||
from ..parent import example
|
|
||||||
|
|
||||||
from ... import grandparent
|
|
||||||
from ...grandparent import example
|
|
||||||
|
|
||||||
import other
|
import other
|
||||||
import other.example
|
import other.example
|
||||||
from other import example
|
from other import example
|
||||||
|
|
||||||
|
# TID252
|
||||||
|
from . import sibling
|
||||||
|
from .sibling import example
|
||||||
|
from .. import parent
|
||||||
|
from ..parent import example
|
||||||
|
from ... import grandparent
|
||||||
|
from ...grandparent import example
|
||||||
|
from .parent import hello
|
||||||
|
from .\
|
||||||
|
parent import \
|
||||||
|
hello_world
|
||||||
|
from \
|
||||||
|
..parent\
|
||||||
|
import \
|
||||||
|
world_hello
|
||||||
|
|
||||||
|
# TID252 (without autofix; too many levels up)
|
||||||
|
from ..... import ultragrantparent
|
||||||
|
from ...... import ultragrantparent
|
||||||
|
from ....... import ultragrantparent
|
||||||
|
from ......... import ultragrantparent
|
||||||
|
from ........................... import ultragrantparent
|
||||||
|
from .....parent import ultragrantparent
|
||||||
|
from .........parent import ultragrantparent
|
||||||
|
from ...........................parent import ultragrantparent
|
||||||
|
|
|
@ -1291,9 +1291,12 @@ where
|
||||||
if self.settings.rules.enabled(&Rule::RelativeImports) {
|
if self.settings.rules.enabled(&Rule::RelativeImports) {
|
||||||
if let Some(diagnostic) =
|
if let Some(diagnostic) =
|
||||||
flake8_tidy_imports::relative_imports::banned_relative_import(
|
flake8_tidy_imports::relative_imports::banned_relative_import(
|
||||||
|
self,
|
||||||
stmt,
|
stmt,
|
||||||
level.as_ref(),
|
level.as_ref(),
|
||||||
|
module.as_deref(),
|
||||||
&self.settings.flake8_tidy_imports.ban_relative_imports,
|
&self.settings.flake8_tidy_imports.ban_relative_imports,
|
||||||
|
self.path,
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
self.diagnostics.push(diagnostic);
|
self.diagnostics.push(diagnostic);
|
||||||
|
|
|
@ -1,11 +1,19 @@
|
||||||
use ruff_macros::{define_violation, derive_message_formats};
|
use std::path::Path;
|
||||||
use rustpython_parser::ast::Stmt;
|
|
||||||
|
use rustpython_parser::ast::{Stmt, StmtKind};
|
||||||
use schemars::JsonSchema;
|
use schemars::JsonSchema;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
use ruff_macros::{define_violation, derive_message_formats};
|
||||||
|
use ruff_python::string::is_lower_with_underscore;
|
||||||
|
|
||||||
|
use crate::ast::helpers::{create_stmt, unparse_stmt};
|
||||||
use crate::ast::types::Range;
|
use crate::ast::types::Range;
|
||||||
|
use crate::checkers::ast::Checker;
|
||||||
|
use crate::fix::Fix;
|
||||||
use crate::registry::Diagnostic;
|
use crate::registry::Diagnostic;
|
||||||
use crate::violation::Violation;
|
use crate::source_code::Stylist;
|
||||||
|
use crate::violation::{AutofixKind, Availability, Violation};
|
||||||
|
|
||||||
pub type Settings = Strictness;
|
pub type Settings = Strictness;
|
||||||
|
|
||||||
|
@ -20,34 +28,144 @@ pub enum Strictness {
|
||||||
}
|
}
|
||||||
|
|
||||||
define_violation!(
|
define_violation!(
|
||||||
pub struct RelativeImports(pub Strictness);
|
/// ## What it does
|
||||||
|
/// Checks for relative imports.
|
||||||
|
///
|
||||||
|
/// ## Why is this bad?
|
||||||
|
/// Absolute imports, or relative imports from siblings, are recommended by [PEP 8](https://peps.python.org/pep-0008/#imports):
|
||||||
|
///
|
||||||
|
/// > 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
|
||||||
|
/// > ```
|
||||||
|
///
|
||||||
|
/// Note that degree of strictness packages can be specified via the
|
||||||
|
/// [`strictness`](https://github.com/charliermarsh/ruff#strictness)
|
||||||
|
/// configuration option, which allows banning all relative imports (`strictness = "all"`)
|
||||||
|
/// or only those that extend into the parent module or beyond (`strictness = "parents"`).
|
||||||
|
///
|
||||||
|
/// ## Example
|
||||||
|
/// ```python
|
||||||
|
/// from .. import foo
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// Use instead:
|
||||||
|
/// ```python
|
||||||
|
/// from mypkg import foo
|
||||||
|
/// ```
|
||||||
|
pub struct RelativeImports {
|
||||||
|
pub strictness: Strictness,
|
||||||
|
}
|
||||||
);
|
);
|
||||||
impl Violation for RelativeImports {
|
impl Violation for RelativeImports {
|
||||||
|
const AUTOFIX: Option<AutofixKind> = Some(AutofixKind::new(Availability::Sometimes));
|
||||||
|
|
||||||
#[derive_message_formats]
|
#[derive_message_formats]
|
||||||
fn message(&self) -> String {
|
fn message(&self) -> String {
|
||||||
let RelativeImports(strictness) = self;
|
match self.strictness {
|
||||||
match strictness {
|
|
||||||
Strictness::Parents => format!("Relative imports from parent modules are banned"),
|
Strictness::Parents => format!("Relative imports from parent modules are banned"),
|
||||||
Strictness::All => format!("Relative imports are banned"),
|
Strictness::All => format!("Relative imports are banned"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn autofix_title_formatter(&self) -> Option<fn(&Self) -> String> {
|
||||||
|
Some(|RelativeImports { strictness }| match strictness {
|
||||||
|
Strictness::Parents => {
|
||||||
|
format!("Replace relative imports from parent modules with absolute imports")
|
||||||
|
}
|
||||||
|
Strictness::All => format!("Replace relative imports with absolute imports"),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn fix_banned_relative_import(
|
||||||
|
stmt: &Stmt,
|
||||||
|
level: Option<&usize>,
|
||||||
|
module: Option<&str>,
|
||||||
|
path: &Path,
|
||||||
|
stylist: &Stylist,
|
||||||
|
) -> Option<Fix> {
|
||||||
|
let base = if let Some(module) = module {
|
||||||
|
module.to_string()
|
||||||
|
} else {
|
||||||
|
String::new()
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut parent = path.parent()?;
|
||||||
|
for _ in 0..*level? {
|
||||||
|
parent = parent.parent()?;
|
||||||
|
}
|
||||||
|
|
||||||
|
let module_name = parent.file_name()?.to_string_lossy().to_string();
|
||||||
|
|
||||||
|
// Require import to be a valid PEP 8 module:
|
||||||
|
// https://python.org/dev/peps/pep-0008/#package-and-module-names
|
||||||
|
if !is_lower_with_underscore(module_name.as_str()) {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
let new_import = if base.is_empty() {
|
||||||
|
module_name
|
||||||
|
} else {
|
||||||
|
format!("{}.{}", module_name, base)
|
||||||
|
};
|
||||||
|
|
||||||
|
let content = match &stmt.node {
|
||||||
|
StmtKind::ImportFrom { names, .. } => unparse_stmt(
|
||||||
|
&create_stmt(StmtKind::ImportFrom {
|
||||||
|
module: Some(new_import),
|
||||||
|
names: names.clone(),
|
||||||
|
level: Some(0),
|
||||||
|
}),
|
||||||
|
stylist,
|
||||||
|
),
|
||||||
|
_ => return None,
|
||||||
|
};
|
||||||
|
|
||||||
|
Some(Fix::replacement(
|
||||||
|
content,
|
||||||
|
stmt.location,
|
||||||
|
stmt.end_location.unwrap(),
|
||||||
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// TID252
|
/// TID252
|
||||||
pub fn banned_relative_import(
|
pub fn banned_relative_import(
|
||||||
|
checker: &Checker,
|
||||||
stmt: &Stmt,
|
stmt: &Stmt,
|
||||||
level: Option<&usize>,
|
level: Option<&usize>,
|
||||||
|
module: Option<&str>,
|
||||||
strictness: &Strictness,
|
strictness: &Strictness,
|
||||||
|
path: &Path,
|
||||||
) -> Option<Diagnostic> {
|
) -> Option<Diagnostic> {
|
||||||
let strictness_level = match strictness {
|
let strictness_level = match strictness {
|
||||||
Strictness::All => 0,
|
Strictness::All => 0,
|
||||||
Strictness::Parents => 1,
|
Strictness::Parents => 1,
|
||||||
};
|
};
|
||||||
if level? > &strictness_level {
|
if level? > &strictness_level {
|
||||||
Some(Diagnostic::new(
|
let mut diagnostic = Diagnostic::new(
|
||||||
RelativeImports(strictness.clone()),
|
RelativeImports {
|
||||||
|
strictness: strictness.clone(),
|
||||||
|
},
|
||||||
Range::from_located(stmt),
|
Range::from_located(stmt),
|
||||||
))
|
);
|
||||||
|
if checker.patch(diagnostic.kind.rule()) {
|
||||||
|
if let Some(fix) =
|
||||||
|
fix_banned_relative_import(stmt, level, module, path, checker.stylist)
|
||||||
|
{
|
||||||
|
diagnostic.amend(fix);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
Some(diagnostic)
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
@ -59,12 +177,13 @@ mod tests {
|
||||||
|
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
|
|
||||||
use super::Strictness;
|
|
||||||
use crate::assert_yaml_snapshot;
|
use crate::assert_yaml_snapshot;
|
||||||
use crate::registry::Rule;
|
use crate::registry::Rule;
|
||||||
use crate::settings::Settings;
|
use crate::settings::Settings;
|
||||||
use crate::test::test_path;
|
use crate::test::test_path;
|
||||||
|
|
||||||
|
use super::Strictness;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn ban_parent_imports() -> Result<()> {
|
fn ban_parent_imports() -> Result<()> {
|
||||||
let diagnostics = test_path(
|
let diagnostics = test_path(
|
||||||
|
|
|
@ -1,65 +1,264 @@
|
||||||
---
|
---
|
||||||
source: src/rules/flake8_tidy_imports/relative_imports.rs
|
source: crates/ruff/src/rules/flake8_tidy_imports/relative_imports.rs
|
||||||
expression: diagnostics
|
expression: diagnostics
|
||||||
---
|
---
|
||||||
- kind:
|
- kind:
|
||||||
RelativeImports: all
|
RelativeImports:
|
||||||
location:
|
strictness: all
|
||||||
row: 1
|
|
||||||
column: 0
|
|
||||||
end_location:
|
|
||||||
row: 1
|
|
||||||
column: 21
|
|
||||||
fix: ~
|
|
||||||
parent: ~
|
|
||||||
- kind:
|
|
||||||
RelativeImports: all
|
|
||||||
location:
|
|
||||||
row: 2
|
|
||||||
column: 0
|
|
||||||
end_location:
|
|
||||||
row: 2
|
|
||||||
column: 28
|
|
||||||
fix: ~
|
|
||||||
parent: ~
|
|
||||||
- kind:
|
|
||||||
RelativeImports: all
|
|
||||||
location:
|
|
||||||
row: 4
|
|
||||||
column: 0
|
|
||||||
end_location:
|
|
||||||
row: 4
|
|
||||||
column: 21
|
|
||||||
fix: ~
|
|
||||||
parent: ~
|
|
||||||
- kind:
|
|
||||||
RelativeImports: all
|
|
||||||
location:
|
|
||||||
row: 5
|
|
||||||
column: 0
|
|
||||||
end_location:
|
|
||||||
row: 5
|
|
||||||
column: 28
|
|
||||||
fix: ~
|
|
||||||
parent: ~
|
|
||||||
- kind:
|
|
||||||
RelativeImports: all
|
|
||||||
location:
|
location:
|
||||||
row: 7
|
row: 7
|
||||||
column: 0
|
column: 0
|
||||||
end_location:
|
end_location:
|
||||||
row: 7
|
row: 7
|
||||||
|
column: 21
|
||||||
|
fix:
|
||||||
|
content:
|
||||||
|
- from fixtures import sibling
|
||||||
|
location:
|
||||||
|
row: 7
|
||||||
|
column: 0
|
||||||
|
end_location:
|
||||||
|
row: 7
|
||||||
|
column: 21
|
||||||
|
parent: ~
|
||||||
|
- kind:
|
||||||
|
RelativeImports:
|
||||||
|
strictness: all
|
||||||
|
location:
|
||||||
|
row: 8
|
||||||
|
column: 0
|
||||||
|
end_location:
|
||||||
|
row: 8
|
||||||
|
column: 28
|
||||||
|
fix:
|
||||||
|
content:
|
||||||
|
- from fixtures.sibling import example
|
||||||
|
location:
|
||||||
|
row: 8
|
||||||
|
column: 0
|
||||||
|
end_location:
|
||||||
|
row: 8
|
||||||
|
column: 28
|
||||||
|
parent: ~
|
||||||
|
- kind:
|
||||||
|
RelativeImports:
|
||||||
|
strictness: all
|
||||||
|
location:
|
||||||
|
row: 9
|
||||||
|
column: 0
|
||||||
|
end_location:
|
||||||
|
row: 9
|
||||||
|
column: 21
|
||||||
|
fix:
|
||||||
|
content:
|
||||||
|
- from test import parent
|
||||||
|
location:
|
||||||
|
row: 9
|
||||||
|
column: 0
|
||||||
|
end_location:
|
||||||
|
row: 9
|
||||||
|
column: 21
|
||||||
|
parent: ~
|
||||||
|
- kind:
|
||||||
|
RelativeImports:
|
||||||
|
strictness: all
|
||||||
|
location:
|
||||||
|
row: 10
|
||||||
|
column: 0
|
||||||
|
end_location:
|
||||||
|
row: 10
|
||||||
|
column: 28
|
||||||
|
fix:
|
||||||
|
content:
|
||||||
|
- from test.parent import example
|
||||||
|
location:
|
||||||
|
row: 10
|
||||||
|
column: 0
|
||||||
|
end_location:
|
||||||
|
row: 10
|
||||||
|
column: 28
|
||||||
|
parent: ~
|
||||||
|
- kind:
|
||||||
|
RelativeImports:
|
||||||
|
strictness: all
|
||||||
|
location:
|
||||||
|
row: 11
|
||||||
|
column: 0
|
||||||
|
end_location:
|
||||||
|
row: 11
|
||||||
column: 27
|
column: 27
|
||||||
fix: ~
|
fix:
|
||||||
|
content:
|
||||||
|
- from resources import grandparent
|
||||||
|
location:
|
||||||
|
row: 11
|
||||||
|
column: 0
|
||||||
|
end_location:
|
||||||
|
row: 11
|
||||||
|
column: 27
|
||||||
parent: ~
|
parent: ~
|
||||||
- kind:
|
- kind:
|
||||||
RelativeImports: all
|
RelativeImports:
|
||||||
|
strictness: all
|
||||||
location:
|
location:
|
||||||
row: 8
|
row: 12
|
||||||
column: 0
|
column: 0
|
||||||
end_location:
|
end_location:
|
||||||
row: 8
|
row: 12
|
||||||
|
column: 34
|
||||||
|
fix:
|
||||||
|
content:
|
||||||
|
- from resources.grandparent import example
|
||||||
|
location:
|
||||||
|
row: 12
|
||||||
|
column: 0
|
||||||
|
end_location:
|
||||||
|
row: 12
|
||||||
|
column: 34
|
||||||
|
parent: ~
|
||||||
|
- kind:
|
||||||
|
RelativeImports:
|
||||||
|
strictness: all
|
||||||
|
location:
|
||||||
|
row: 13
|
||||||
|
column: 0
|
||||||
|
end_location:
|
||||||
|
row: 13
|
||||||
|
column: 26
|
||||||
|
fix:
|
||||||
|
content:
|
||||||
|
- from fixtures.parent import hello
|
||||||
|
location:
|
||||||
|
row: 13
|
||||||
|
column: 0
|
||||||
|
end_location:
|
||||||
|
row: 13
|
||||||
|
column: 26
|
||||||
|
parent: ~
|
||||||
|
- kind:
|
||||||
|
RelativeImports:
|
||||||
|
strictness: all
|
||||||
|
location:
|
||||||
|
row: 14
|
||||||
|
column: 0
|
||||||
|
end_location:
|
||||||
|
row: 16
|
||||||
|
column: 19
|
||||||
|
fix:
|
||||||
|
content:
|
||||||
|
- from fixtures.parent import hello_world
|
||||||
|
location:
|
||||||
|
row: 14
|
||||||
|
column: 0
|
||||||
|
end_location:
|
||||||
|
row: 16
|
||||||
|
column: 19
|
||||||
|
parent: ~
|
||||||
|
- kind:
|
||||||
|
RelativeImports:
|
||||||
|
strictness: all
|
||||||
|
location:
|
||||||
|
row: 17
|
||||||
|
column: 0
|
||||||
|
end_location:
|
||||||
|
row: 20
|
||||||
|
column: 15
|
||||||
|
fix:
|
||||||
|
content:
|
||||||
|
- from test.parent import world_hello
|
||||||
|
location:
|
||||||
|
row: 17
|
||||||
|
column: 0
|
||||||
|
end_location:
|
||||||
|
row: 20
|
||||||
|
column: 15
|
||||||
|
parent: ~
|
||||||
|
- kind:
|
||||||
|
RelativeImports:
|
||||||
|
strictness: all
|
||||||
|
location:
|
||||||
|
row: 23
|
||||||
|
column: 0
|
||||||
|
end_location:
|
||||||
|
row: 23
|
||||||
column: 34
|
column: 34
|
||||||
fix: ~
|
fix: ~
|
||||||
parent: ~
|
parent: ~
|
||||||
|
- kind:
|
||||||
|
RelativeImports:
|
||||||
|
strictness: all
|
||||||
|
location:
|
||||||
|
row: 24
|
||||||
|
column: 0
|
||||||
|
end_location:
|
||||||
|
row: 24
|
||||||
|
column: 35
|
||||||
|
fix: ~
|
||||||
|
parent: ~
|
||||||
|
- kind:
|
||||||
|
RelativeImports:
|
||||||
|
strictness: all
|
||||||
|
location:
|
||||||
|
row: 25
|
||||||
|
column: 0
|
||||||
|
end_location:
|
||||||
|
row: 25
|
||||||
|
column: 36
|
||||||
|
fix: ~
|
||||||
|
parent: ~
|
||||||
|
- kind:
|
||||||
|
RelativeImports:
|
||||||
|
strictness: all
|
||||||
|
location:
|
||||||
|
row: 26
|
||||||
|
column: 0
|
||||||
|
end_location:
|
||||||
|
row: 26
|
||||||
|
column: 38
|
||||||
|
fix: ~
|
||||||
|
parent: ~
|
||||||
|
- kind:
|
||||||
|
RelativeImports:
|
||||||
|
strictness: all
|
||||||
|
location:
|
||||||
|
row: 27
|
||||||
|
column: 0
|
||||||
|
end_location:
|
||||||
|
row: 27
|
||||||
|
column: 56
|
||||||
|
fix: ~
|
||||||
|
parent: ~
|
||||||
|
- kind:
|
||||||
|
RelativeImports:
|
||||||
|
strictness: all
|
||||||
|
location:
|
||||||
|
row: 28
|
||||||
|
column: 0
|
||||||
|
end_location:
|
||||||
|
row: 28
|
||||||
|
column: 40
|
||||||
|
fix: ~
|
||||||
|
parent: ~
|
||||||
|
- kind:
|
||||||
|
RelativeImports:
|
||||||
|
strictness: all
|
||||||
|
location:
|
||||||
|
row: 29
|
||||||
|
column: 0
|
||||||
|
end_location:
|
||||||
|
row: 29
|
||||||
|
column: 44
|
||||||
|
fix: ~
|
||||||
|
parent: ~
|
||||||
|
- kind:
|
||||||
|
RelativeImports:
|
||||||
|
strictness: all
|
||||||
|
location:
|
||||||
|
row: 30
|
||||||
|
column: 0
|
||||||
|
end_location:
|
||||||
|
row: 30
|
||||||
|
column: 62
|
||||||
|
fix: ~
|
||||||
|
parent: ~
|
||||||
|
|
||||||
|
|
|
@ -1,45 +1,188 @@
|
||||||
---
|
---
|
||||||
source: src/rules/flake8_tidy_imports/relative_imports.rs
|
source: crates/ruff/src/rules/flake8_tidy_imports/relative_imports.rs
|
||||||
expression: diagnostics
|
expression: diagnostics
|
||||||
---
|
---
|
||||||
- kind:
|
- kind:
|
||||||
RelativeImports: parents
|
RelativeImports:
|
||||||
|
strictness: parents
|
||||||
location:
|
location:
|
||||||
row: 4
|
row: 9
|
||||||
column: 0
|
column: 0
|
||||||
end_location:
|
end_location:
|
||||||
row: 4
|
row: 9
|
||||||
column: 21
|
column: 21
|
||||||
fix: ~
|
fix:
|
||||||
|
content:
|
||||||
|
- from test import parent
|
||||||
|
location:
|
||||||
|
row: 9
|
||||||
|
column: 0
|
||||||
|
end_location:
|
||||||
|
row: 9
|
||||||
|
column: 21
|
||||||
parent: ~
|
parent: ~
|
||||||
- kind:
|
- kind:
|
||||||
RelativeImports: parents
|
RelativeImports:
|
||||||
|
strictness: parents
|
||||||
location:
|
location:
|
||||||
row: 5
|
row: 10
|
||||||
column: 0
|
column: 0
|
||||||
end_location:
|
end_location:
|
||||||
row: 5
|
row: 10
|
||||||
column: 28
|
column: 28
|
||||||
fix: ~
|
fix:
|
||||||
|
content:
|
||||||
|
- from test.parent import example
|
||||||
|
location:
|
||||||
|
row: 10
|
||||||
|
column: 0
|
||||||
|
end_location:
|
||||||
|
row: 10
|
||||||
|
column: 28
|
||||||
parent: ~
|
parent: ~
|
||||||
- kind:
|
- kind:
|
||||||
RelativeImports: parents
|
RelativeImports:
|
||||||
|
strictness: parents
|
||||||
location:
|
location:
|
||||||
row: 7
|
row: 11
|
||||||
column: 0
|
column: 0
|
||||||
end_location:
|
end_location:
|
||||||
row: 7
|
row: 11
|
||||||
column: 27
|
column: 27
|
||||||
fix: ~
|
fix:
|
||||||
|
content:
|
||||||
|
- from resources import grandparent
|
||||||
|
location:
|
||||||
|
row: 11
|
||||||
|
column: 0
|
||||||
|
end_location:
|
||||||
|
row: 11
|
||||||
|
column: 27
|
||||||
parent: ~
|
parent: ~
|
||||||
- kind:
|
- kind:
|
||||||
RelativeImports: parents
|
RelativeImports:
|
||||||
|
strictness: parents
|
||||||
location:
|
location:
|
||||||
row: 8
|
row: 12
|
||||||
column: 0
|
column: 0
|
||||||
end_location:
|
end_location:
|
||||||
row: 8
|
row: 12
|
||||||
|
column: 34
|
||||||
|
fix:
|
||||||
|
content:
|
||||||
|
- from resources.grandparent import example
|
||||||
|
location:
|
||||||
|
row: 12
|
||||||
|
column: 0
|
||||||
|
end_location:
|
||||||
|
row: 12
|
||||||
|
column: 34
|
||||||
|
parent: ~
|
||||||
|
- kind:
|
||||||
|
RelativeImports:
|
||||||
|
strictness: parents
|
||||||
|
location:
|
||||||
|
row: 17
|
||||||
|
column: 0
|
||||||
|
end_location:
|
||||||
|
row: 20
|
||||||
|
column: 15
|
||||||
|
fix:
|
||||||
|
content:
|
||||||
|
- from test.parent import world_hello
|
||||||
|
location:
|
||||||
|
row: 17
|
||||||
|
column: 0
|
||||||
|
end_location:
|
||||||
|
row: 20
|
||||||
|
column: 15
|
||||||
|
parent: ~
|
||||||
|
- kind:
|
||||||
|
RelativeImports:
|
||||||
|
strictness: parents
|
||||||
|
location:
|
||||||
|
row: 23
|
||||||
|
column: 0
|
||||||
|
end_location:
|
||||||
|
row: 23
|
||||||
column: 34
|
column: 34
|
||||||
fix: ~
|
fix: ~
|
||||||
parent: ~
|
parent: ~
|
||||||
|
- kind:
|
||||||
|
RelativeImports:
|
||||||
|
strictness: parents
|
||||||
|
location:
|
||||||
|
row: 24
|
||||||
|
column: 0
|
||||||
|
end_location:
|
||||||
|
row: 24
|
||||||
|
column: 35
|
||||||
|
fix: ~
|
||||||
|
parent: ~
|
||||||
|
- kind:
|
||||||
|
RelativeImports:
|
||||||
|
strictness: parents
|
||||||
|
location:
|
||||||
|
row: 25
|
||||||
|
column: 0
|
||||||
|
end_location:
|
||||||
|
row: 25
|
||||||
|
column: 36
|
||||||
|
fix: ~
|
||||||
|
parent: ~
|
||||||
|
- kind:
|
||||||
|
RelativeImports:
|
||||||
|
strictness: parents
|
||||||
|
location:
|
||||||
|
row: 26
|
||||||
|
column: 0
|
||||||
|
end_location:
|
||||||
|
row: 26
|
||||||
|
column: 38
|
||||||
|
fix: ~
|
||||||
|
parent: ~
|
||||||
|
- kind:
|
||||||
|
RelativeImports:
|
||||||
|
strictness: parents
|
||||||
|
location:
|
||||||
|
row: 27
|
||||||
|
column: 0
|
||||||
|
end_location:
|
||||||
|
row: 27
|
||||||
|
column: 56
|
||||||
|
fix: ~
|
||||||
|
parent: ~
|
||||||
|
- kind:
|
||||||
|
RelativeImports:
|
||||||
|
strictness: parents
|
||||||
|
location:
|
||||||
|
row: 28
|
||||||
|
column: 0
|
||||||
|
end_location:
|
||||||
|
row: 28
|
||||||
|
column: 40
|
||||||
|
fix: ~
|
||||||
|
parent: ~
|
||||||
|
- kind:
|
||||||
|
RelativeImports:
|
||||||
|
strictness: parents
|
||||||
|
location:
|
||||||
|
row: 29
|
||||||
|
column: 0
|
||||||
|
end_location:
|
||||||
|
row: 29
|
||||||
|
column: 44
|
||||||
|
fix: ~
|
||||||
|
parent: ~
|
||||||
|
- kind:
|
||||||
|
RelativeImports:
|
||||||
|
strictness: parents
|
||||||
|
location:
|
||||||
|
row: 30
|
||||||
|
column: 0
|
||||||
|
end_location:
|
||||||
|
row: 30
|
||||||
|
column: 62
|
||||||
|
fix: ~
|
||||||
|
parent: ~
|
||||||
|
|
||||||
|
|
|
@ -3,6 +3,7 @@ use regex::Regex;
|
||||||
|
|
||||||
pub static STRING_QUOTE_PREFIX_REGEX: Lazy<Regex> =
|
pub static STRING_QUOTE_PREFIX_REGEX: Lazy<Regex> =
|
||||||
Lazy::new(|| Regex::new(r#"^(?i)[urb]*['"](?P<raw>.*)['"]$"#).unwrap());
|
Lazy::new(|| Regex::new(r#"^(?i)[urb]*['"](?P<raw>.*)['"]$"#).unwrap());
|
||||||
|
pub static LOWER_OR_UNDERSCORE: Lazy<Regex> = Lazy::new(|| Regex::new(r"^[a-z_]+$").unwrap());
|
||||||
|
|
||||||
pub fn is_lower(s: &str) -> bool {
|
pub fn is_lower(s: &str) -> bool {
|
||||||
let mut cased = false;
|
let mut cased = false;
|
||||||
|
@ -28,6 +29,11 @@ pub fn is_upper(s: &str) -> bool {
|
||||||
cased
|
cased
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Module names should be lowercase, and may contain underscore
|
||||||
|
pub fn is_lower_with_underscore(s: &str) -> bool {
|
||||||
|
LOWER_OR_UNDERSCORE.is_match(s)
|
||||||
|
}
|
||||||
|
|
||||||
/// Remove prefixes (u, r, b) and quotes around a string. This expects the given
|
/// Remove prefixes (u, r, b) and quotes around a string. This expects the given
|
||||||
/// string to be a valid Python string representation, it doesn't do any
|
/// string to be a valid Python string representation, it doesn't do any
|
||||||
/// validation.
|
/// validation.
|
||||||
|
@ -43,7 +49,7 @@ pub fn strip_quotes_and_prefixes(s: &str) -> &str {
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use crate::string::{is_lower, is_upper, strip_quotes_and_prefixes};
|
use crate::string::{is_lower, is_lower_with_underscore, is_upper, strip_quotes_and_prefixes};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_is_lower() {
|
fn test_is_lower() {
|
||||||
|
@ -56,6 +62,14 @@ mod tests {
|
||||||
assert!(!is_lower("_"));
|
assert!(!is_lower("_"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_is_lower_underscore() {
|
||||||
|
assert!(is_lower_with_underscore("abc"));
|
||||||
|
assert!(is_lower_with_underscore("a_b_c"));
|
||||||
|
assert!(!is_lower_with_underscore("a-b-c"));
|
||||||
|
assert!(!is_lower_with_underscore("a_B_c"));
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_is_upper() {
|
fn test_is_upper() {
|
||||||
assert!(is_upper("ABC"));
|
assert!(is_upper("ABC"));
|
||||||
|
|
40
docs/rules/relative-imports.md
Normal file
40
docs/rules/relative-imports.md
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
# relative-imports (TID252)
|
||||||
|
|
||||||
|
Derived from the **flake8-tidy-imports** linter.
|
||||||
|
|
||||||
|
Autofix is sometimes available.
|
||||||
|
|
||||||
|
## What it does
|
||||||
|
Checks for relative imports.
|
||||||
|
|
||||||
|
## Why is this bad?
|
||||||
|
Absolute imports, or relative imports from siblings, are recommended by [PEP 8](https://peps.python.org/pep-0008/#imports):
|
||||||
|
|
||||||
|
> 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
|
||||||
|
> ```
|
||||||
|
|
||||||
|
Note that degree of strictness packages can be specified via the
|
||||||
|
[`strictness`](https://github.com/charliermarsh/ruff#strictness)
|
||||||
|
configuration option, which allows banning all relative imports (`strictness = "all"`)
|
||||||
|
or only those that extend into the parent module or beyond (`strictness = "parents"`).
|
||||||
|
|
||||||
|
## Example
|
||||||
|
```python
|
||||||
|
from .. import foo
|
||||||
|
```
|
||||||
|
|
||||||
|
Use instead:
|
||||||
|
```python
|
||||||
|
from mypkg import foo
|
||||||
|
```
|
Loading…
Add table
Add a link
Reference in a new issue