Skip to content

Commit 62591e4

Browse files
committed
Python: Avoid duplicate modules in points-to and resulting blow-up.
1 parent 87ebc17 commit 62591e4

File tree

8 files changed

+44
-2
lines changed

8 files changed

+44
-2
lines changed

python/ql/src/semmle/python/Module.qll

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,7 @@ class Module extends Module_, Scope, AstNode {
195195

196196
}
197197

198+
198199
bindingset[name]
199200
private predicate legalDottedName(string name) {
200201
name.regexpMatch("(\\p{L}|_)(\\p{L}|\\d|_)*(\\.(\\p{L}|_)(\\p{L}|\\d|_)*)*")
@@ -244,3 +245,30 @@ private predicate isStubRoot(Folder f) {
244245
f.getAbsolutePath().matches("%/data/python/stubs")
245246
}
246247

248+
249+
/** Holds if the Container `c` should be the preferred file or folder for
250+
* the given name when performing imports.
251+
* Trivially true for any container if it is the only one with its name.
252+
* However, if there are several modules with the same name, then
253+
* this is the module most likely to be imported under that name.
254+
*/
255+
predicate isPreferredModuleForName(Container c, string name) {
256+
exists(int p |
257+
p = min(int x | x = priorityForName(_, name)) and
258+
p = priorityForName(c, name)
259+
)
260+
}
261+
262+
private int priorityForName(Container c, string name) {
263+
name = moduleNameFromFile(c) and
264+
(
265+
// In the source
266+
exists(c.getRelativePath()) and result = -1
267+
or
268+
// On an import path
269+
exists(c.getImportRoot(result))
270+
or
271+
// Otherwise
272+
result = 10000
273+
)
274+
}

python/ql/src/semmle/python/objects/TObject.qll

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,12 +47,13 @@ cached newtype TObject =
4747
or
4848
/* Package objects */
4949
TPackageObject(Folder f) {
50-
exists(moduleNameFromFile(f))
50+
isPreferredModuleForName(f, _)
5151
}
5252
or
5353
/* Python module objects */
5454
TPythonModule(Module m) {
55-
not m.isPackage() and not exists(SyntaxError se | se.getFile() = m.getFile())
55+
not m.isPackage() and isPreferredModuleForName(m.getFile(), _) and
56+
not exists(SyntaxError se | se.getFile() = m.getFile())
5657
}
5758
or
5859
/* `True` */
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
| sqlite3 | 2 | 1 |
2+
| sqlite3.__init__ | 2 | 1 |
3+
| sqlite3.dump | 2 | 1 |
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
2+
import python
3+
4+
from string name, int mcnt
5+
where mcnt = strictcount(Module m | m.getName() = name) and mcnt > 1
6+
select name, mcnt, strictcount(ModuleValue val | val.getName() = name)
7+
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
semmle-extractor-options: -R .
2+
optimize: true
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
import sqlite3.dump

python/ql/test/library-tests/modules/duplicate_name/venv/sqlite3/__init__.py

Whitespace-only changes.

python/ql/test/library-tests/modules/duplicate_name/venv/sqlite3/dump.py

Whitespace-only changes.

0 commit comments

Comments
 (0)