Skip to content

Commit

Permalink
[pycodestyle] Exempt site.addsitedir(...) calls (E402) (#16251)
Browse files Browse the repository at this point in the history
  • Loading branch information
VascoSch92 authored Feb 19, 2025
1 parent 3ea32e2 commit 3032867
Show file tree
Hide file tree
Showing 5 changed files with 45 additions and 1 deletion.
19 changes: 19 additions & 0 deletions crates/ruff_linter/resources/test/fixtures/pycodestyle/E402_5.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Issue: https://github.com/astral-sh/ruff/issues/16247#event-16362806498

import os
import site
import sys
import sysconfig

site.addsitedir(
os.path.join(
os.path.dirname(os.path.dirname(__file__)),
sysconfig.get_path("purelib", vars={"base": "."}),
)
)

from mypkg.__main__ import main

if __name__ == "__main__":
sys.argv[0] = sys.argv[0].removesuffix(".py")
sys.exit(main())
3 changes: 2 additions & 1 deletion crates/ruff_linter/src/checkers/ast/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -556,7 +556,8 @@ impl<'a> Visitor<'a> for Checker<'a> {
|| imports::is_matplotlib_activation(stmt, self.semantic())
|| imports::is_sys_path_modification(stmt, self.semantic())
|| imports::is_os_environ_modification(stmt, self.semantic())
|| imports::is_pytest_importorskip(stmt, self.semantic()))
|| imports::is_pytest_importorskip(stmt, self.semantic())
|| imports::is_site_sys_path_modification(stmt, self.semantic()))
{
self.semantic.flags |= SemanticModelFlags::IMPORT_BOUNDARY;
}
Expand Down
1 change: 1 addition & 0 deletions crates/ruff_linter/src/rules/pycodestyle/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ mod tests {
#[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_4.py"))]
#[test_case(Rule::ModuleImportNotAtTopOfFile, Path::new("E402_5.py"))]
#[test_case(Rule::ModuleImportNotAtTopOfFile, Path::new("E402.ipynb"))]
#[test_case(Rule::MultipleImportsOnOneLine, Path::new("E40.py"))]
#[test_case(Rule::MultipleStatementsOnOneLineColon, Path::new("E70.py"))]
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
---
source: crates/ruff_linter/src/rules/pycodestyle/mod.rs
---
20 changes: 20 additions & 0 deletions crates/ruff_python_semantic/src/analyze/imports.rs
Original file line number Diff line number Diff line change
Expand Up @@ -127,3 +127,23 @@ pub fn is_pytest_importorskip(stmt: &Stmt, semantic: &SemanticModel) -> bool {
matches!(qualified_name.segments(), ["pytest", "importorskip"])
})
}

/// Returns `true` if a [`Stmt`] is a dynamic modification of the Python
/// module search path, e.g.,
/// ```python
/// import site
///
/// site.addsitedir(...)
/// ```
pub fn is_site_sys_path_modification(stmt: &Stmt, semantic: &SemanticModel) -> bool {
if let Stmt::Expr(ast::StmtExpr { value, .. }) = stmt {
if let Expr::Call(ast::ExprCall { func, .. }) = value.as_ref() {
return semantic
.resolve_qualified_name(func.as_ref())
.is_some_and(|qualified_name| {
matches!(qualified_name.segments(), ["site", "addsitedir"])
});
}
}
false
}

0 comments on commit 3032867

Please sign in to comment.