Skip to content
Merged
Changes from 1 commit
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
f2a50a2
test: add coverage for dynamic import with importlib
lmmx Jul 27, 2024
735b43f
style: lint
lmmx Jul 27, 2024
709acb8
feat(WIP): attempt to get importlib imports
lmmx Jul 27, 2024
025df97
test: add test coverage for both importlib formats
lmmx Jul 27, 2024
73c790a
feat: also handle case where importlib.import_module is aliased
lmmx Jul 27, 2024
88b10b4
refactor: consolidate proof of concept test into existing test
lmmx Jul 27, 2024
05cf8ea
refactor(clippy): collapse nested `if let` statements in `Stmt::Expr`…
lmmx Jul 27, 2024
0a0245a
docs: note which sorts of `importlib` import are supported
lmmx Jul 27, 2024
436b292
Merge branch 'main' into main
lmmx Jul 27, 2024
eb2116b
test: check nested packages with import_module resolve to the top lev…
lmmx Jul 27, 2024
2c50759
test: test coverage for aliased import (not being collected)
lmmx Jul 27, 2024
1aabac6
fix: working import alias coverage
lmmx Jul 27, 2024
1b60cf1
feat: full import path so importlib aliases are acknowledged too
lmmx Jul 27, 2024
9008d49
docs: Clarify cases where `importlib.import_module` usage is/is not d…
lmmx Jul 27, 2024
09439b7
docs: docstring for `ImportVisitor` explaining what `import_module_na…
lmmx Jul 27, 2024
3c27b8f
refactor: split long match statement arms into new functions with doc…
lmmx Jul 27, 2024
541a29e
style(consistency): Add language hint to code block in docs/usage.md
lmmx Jul 27, 2024
bacb82d
docs: Link to importlib stdlib docs URL in docs/usage.md
lmmx Jul 27, 2024
80062c8
fix(consistency): Use full language hint on code block in docs/usage.md
lmmx Jul 27, 2024
5060df7
fix(typo): No capitalisation in sentence punctuated by code block in …
lmmx Jul 27, 2024
cb79798
fix(typo): wrong type names used in refactored function
lmmx Jul 27, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
fix: working import alias coverage
  • Loading branch information
lmmx committed Jul 27, 2024
commit 1aabac6bf4922dacb16aa1302edb9d426c50e2d2
43 changes: 16 additions & 27 deletions src/visitor.rs
Original file line number Diff line number Diff line change
@@ -1,19 +1,19 @@
use ruff_python_ast::visitor::{walk_stmt, Visitor};
use ruff_python_ast::{self, Expr, ExprAttribute, ExprName, Stmt, StmtIf};
use ruff_text_size::TextRange;
use std::collections::HashMap;
use std::collections::{HashMap, HashSet};

#[derive(Debug, Clone)]
pub struct ImportVisitor {
imports: HashMap<String, Vec<TextRange>>,
import_module_name: Option<String>,
import_module_names: HashSet<String>,
}

impl ImportVisitor {
pub fn new() -> Self {
Self {
imports: HashMap::new(),
import_module_name: None,
import_module_names: HashSet::new(),
}
}

Expand All @@ -34,7 +34,7 @@ impl<'a> Visitor<'a> for ImportVisitor {
.push(alias.range);

if alias.name.as_str() == "importlib" {
self.import_module_name = Some("import_module".to_string());
self.import_module_names.insert("import_module".to_string());
}
}
}
Expand All @@ -50,46 +50,32 @@ impl<'a> Visitor<'a> for ImportVisitor {
if module_name == "importlib" {
for alias in &import_from_stmt.names {
if alias.name.as_str() == "import_module" {
self.import_module_name = Some(
alias
.asname
.as_ref()
.map(|id| id.as_str().to_string())
.unwrap_or_else(|| "import_module".to_string()),
);
break;
let name = alias
.asname
.as_ref()
.map(|id| id.as_str().to_string())
.unwrap_or_else(|| "import_module".to_string());
self.import_module_names.insert(name);
}
}
}
}
}
}
Stmt::If(if_stmt) if is_guarded_by_type_checking(if_stmt) => {
// Avoid parsing imports that are only evaluated by type checkers.
}
Stmt::Expr(expr_stmt) => {
if let Expr::Call(call_expr) = expr_stmt.value.as_ref() {
let is_import_module = match call_expr.func.as_ref() {
Expr::Attribute(attr_expr) => {
// Case: importlib.import_module(...)
matches!(attr_expr.value.as_ref(), Expr::Name(name) if name.id.as_str() == "importlib")
&& attr_expr.attr.as_str() == "import_module"
}
Expr::Name(name) => {
// Case: import_module(...) or aliased version
self.import_module_name
.as_ref()
.map_or(false, |im_name| name.id.as_str() == im_name)
}
Expr::Name(name) => self.import_module_names.contains(name.id.as_str()),
_ => false,
};

if is_import_module {
if let Some(Expr::StringLiteral(string_literal)) =
call_expr.arguments.args.first()
{
let top_level_module =
get_top_level_module_name(&string_literal.value.to_string());
if let Some(Expr::StringLiteral(string_literal)) = call_expr.arguments.args.first() {
let top_level_module = get_top_level_module_name(&string_literal.value.to_string());
self.imports
.entry(top_level_module)
.or_default()
Expand All @@ -98,6 +84,9 @@ impl<'a> Visitor<'a> for ImportVisitor {
}
}
}
Stmt::If(if_stmt) if is_guarded_by_type_checking(if_stmt) => {
// Avoid parsing imports that are only evaluated by type checkers.
}
_ => walk_stmt(self, stmt), // Delegate other statements to walk_stmt
}
}
Expand Down