[pycodestyle] Exempt sys.path += ... calls (E402) (#15980)

## Summary

The PR addresses issue #15886 .
This commit is contained in:
Vasco Schiavo 2025-02-06 08:51:51 +01:00 committed by GitHub
parent d0555f7b5c
commit 24bab7e82e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 40 additions and 25 deletions

View file

@ -0,0 +1,7 @@
import os
import sys
sys.path += [os.path.dirname(__file__)]
sys.path += ["../"]
from package import module

View file

@ -44,6 +44,7 @@ mod tests {
#[test_case(Rule::ModuleImportNotAtTopOfFile, Path::new("E402_1.py"))] #[test_case(Rule::ModuleImportNotAtTopOfFile, Path::new("E402_1.py"))]
#[test_case(Rule::ModuleImportNotAtTopOfFile, Path::new("E402_2.py"))] #[test_case(Rule::ModuleImportNotAtTopOfFile, Path::new("E402_2.py"))]
#[test_case(Rule::ModuleImportNotAtTopOfFile, Path::new("E402_3.py"))] #[test_case(Rule::ModuleImportNotAtTopOfFile, Path::new("E402_3.py"))]
#[test_case(Rule::ModuleImportNotAtTopOfFile, Path::new("E402_4.py"))]
#[test_case(Rule::ModuleImportNotAtTopOfFile, Path::new("E402.ipynb"))] #[test_case(Rule::ModuleImportNotAtTopOfFile, Path::new("E402.ipynb"))]
#[test_case(Rule::MultipleImportsOnOneLine, Path::new("E40.py"))] #[test_case(Rule::MultipleImportsOnOneLine, Path::new("E40.py"))]
#[test_case(Rule::MultipleStatementsOnOneLineColon, Path::new("E70.py"))] #[test_case(Rule::MultipleStatementsOnOneLineColon, Path::new("E70.py"))]

View file

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

View file

@ -8,33 +8,37 @@ use crate::SemanticModel;
/// import sys /// import sys
/// ///
/// sys.path.append("../") /// sys.path.append("../")
/// sys.path += ["../"]
/// ``` /// ```
pub fn is_sys_path_modification(stmt: &Stmt, semantic: &SemanticModel) -> bool { pub fn is_sys_path_modification(stmt: &Stmt, semantic: &SemanticModel) -> bool {
let Stmt::Expr(ast::StmtExpr { value, range: _ }) = stmt else { match stmt {
return false; Stmt::Expr(ast::StmtExpr { value, range: _ }) => match value.as_ref() {
}; Expr::Call(ast::ExprCall { func, .. }) => semantic
let Expr::Call(ast::ExprCall { func, .. }) = value.as_ref() else { .resolve_qualified_name(func.as_ref())
return false; .is_some_and(|qualified_name| {
}; matches!(
semantic qualified_name.segments(),
.resolve_qualified_name(func.as_ref()) [
.is_some_and(|qualified_name| { "sys",
matches!( "path",
qualified_name.segments(), "append"
[ | "insert"
"sys", | "extend"
"path", | "remove"
"append" | "pop"
| "insert" | "clear"
| "extend" | "reverse"
| "remove" | "sort"
| "pop" ]
| "clear" )
| "reverse" }),
| "sort" _ => false,
] },
) Stmt::AugAssign(ast::StmtAugAssign { target, .. }) => semantic
}) .resolve_qualified_name(map_subscript(target))
.is_some_and(|qualified_name| matches!(qualified_name.segments(), ["sys", "path"])),
_ => false,
}
} }
/// Returns `true` if a [`Stmt`] is an `os.environ` modification, as in: /// Returns `true` if a [`Stmt`] is an `os.environ` modification, as in: