Skip to content

Commit e16bb22

Browse files
committed
Python: Fix resolution of relative imports from namespace packages
The fix may look a bit obscure, so here's what's going on. When we see `from . import helper`, we create an `ImportExpr` with level equal to 1 (corresponding to the number of dots). To resolve such imports, we compute the name of the enclosing package, as part of `ImportExpr.qualifiedTopName()`. For this form of import expression, it is equivalent to `this.getEnclosingModule().getPackageName()`. But `qualifiedTopName` requires that `valid_module_name` holds for its result, and this was _not_ the case for namespace packages. To fix this, we extend `valid_module_name` to include the module names of _any_ folder, not just regular package (which are the ones where there's a `__init__.py` in the folder). Note that this doesn't simply include all folders -- only the ones that result in valid module names in Python.
1 parent 48bf4fd commit e16bb22

File tree

2 files changed

+5
-1
lines changed
  • python/ql

2 files changed

+5
-1
lines changed

python/ql/lib/semmle/python/Import.qll

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,10 @@ private predicate valid_module_name(string name) {
1717
exists(Module m | m.getName() = name)
1818
or
1919
exists(Builtin cmod | cmod.getClass() = Builtin::special("ModuleType") and cmod.getName() = name)
20+
or
21+
// Namespace packages may not have a corresponding Module entity,
22+
// but their names are still valid for the purpose of import resolution.
23+
name = moduleNameFromFile(any(Folder f))
2024
}
2125

2226
/** An artificial expression representing an import */
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
11
def process(value):
2-
sink(value) #$ MISSING: prints=source
2+
sink(value) #$ prints=source

0 commit comments

Comments
 (0)