Skip to content

Commit 8134804

Browse files
committed
Python: Fix missing module resolution
This was due to bad manual magic: restricting the attribute name makes sense when we're talking about submodules of a package, but it doesn't when we're talking about reexported modules. Also (hopefully) fixes the tests so that the Python 3-specific bits are ignored under Python 2.
1 parent 19261ec commit 8134804

File tree

3 files changed

+37
-10
lines changed

3 files changed

+37
-10
lines changed

python/ql/lib/semmle/python/dataflow/new/internal/ImportResolution.qll

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -235,9 +235,12 @@ module ImportResolution {
235235
result = ar
236236
|
237237
isPreferredModuleForName(m.getFile(), p.getPackageName() + "." + attr_name + ["", ".__init__"])
238-
or
239-
// This is also true for attributes that come from reexports.
240-
module_reexport(p, attr_name, m)
238+
)
239+
or
240+
// This is also true for attributes that come from reexports.
241+
exists(Module reexporter, string attr_name |
242+
result.(DataFlow::AttrRead).accesses(getModuleReference(reexporter), attr_name) and
243+
module_reexport(reexporter, attr_name, m)
241244
)
242245
or
243246
// Submodules that are implicitly defined with relative imports of the form `from .foo import ...`.

python/ql/test/experimental/import-resolution/importflow.ql

Lines changed: 28 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -74,15 +74,15 @@ class ResolutionTest extends InlineExpectationsTest {
7474
(
7575
exists(DataFlow::PathNode source, DataFlow::PathNode sink, ImportConfiguration config |
7676
config.hasFlowPath(source, sink) and
77-
correct_version(sink.getNode()) and
77+
not sink.getNode() instanceof VersionGuardedNode and
7878
tag = "prints" and
7979
location = sink.getNode().getLocation() and
8080
value = source.getNode().(SourceString).getContents() and
8181
element = sink.getNode().toString()
8282
)
8383
or
8484
exists(ModuleRef ref |
85-
correct_version(ref) and
85+
not ref instanceof VersionGuardedNode and
8686
ref instanceof CheckArgument and
8787
tag = "prints" and
8888
location = ref.getLocation() and
@@ -93,7 +93,30 @@ class ResolutionTest extends InlineExpectationsTest {
9393
}
9494
}
9595

96-
private predicate correct_version(DataFlow::Node n) {
97-
not n instanceof VersionGuardedNode or
98-
n.(VersionGuardedNode).getVersion() = major_version()
96+
class ResolutionTest3 extends InlineExpectationsTest {
97+
ResolutionTest3() { this = "ResolutionTest3" }
98+
99+
override string getARelevantTag() { result = "prints3" }
100+
101+
override predicate hasActualResult(Location location, string element, string tag, string value) {
102+
(
103+
exists(DataFlow::PathNode source, DataFlow::PathNode sink, ImportConfiguration config |
104+
config.hasFlowPath(source, sink) and
105+
sink.getNode().(VersionGuardedNode).getVersion() = 3 and
106+
tag = "prints3" and
107+
location = sink.getNode().getLocation() and
108+
value = source.getNode().(SourceString).getContents() and
109+
element = sink.getNode().toString()
110+
)
111+
or
112+
exists(ModuleRef ref |
113+
ref.(VersionGuardedNode).getVersion() = 3 and
114+
ref instanceof CheckArgument and
115+
tag = "prints3" and
116+
location = ref.getLocation() and
117+
value = "\"<module " + ref.getName() + ">\"" and
118+
element = ref.toString()
119+
)
120+
)
121+
}
99122
}

python/ql/test/experimental/import-resolution/main.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,8 @@
3535
check("foo_alias.foo_attr", foo_alias.foo_attr, "foo_attr", globals()) #$ prints=foo_attr
3636

3737
# A reference to a reexported module
38-
check("foo.bar_reexported.bar_attr", foo.bar_reexported.bar_attr, "bar_attr", globals()) #$ MISSING: prints=bar_attr
38+
check("foo.bar_reexported", foo.bar_reexported, "<module bar>", globals()) #$ prints="<module bar>"
39+
check("foo.bar_reexported.bar_attr", foo.bar_reexported.bar_attr, "bar_attr", globals()) #$ prints=bar_attr
3940

4041
# A simple "import from" statement.
4142
from bar import bar_attr
@@ -71,7 +72,7 @@ def local_import():
7172
if sys.version_info[0] == 3:
7273
# Importing from a namespace module.
7374
from namespace_package.namespace_module import namespace_module_attr
74-
check("namespace_module_attr", namespace_module_attr, "namespace_module_attr", globals()) #$ prints=namespace_module_attr
75+
check("namespace_module_attr", namespace_module_attr, "namespace_module_attr", globals()) #$ prints3=namespace_module_attr
7576

7677

7778
from attr_clash import clashing_attr, non_clashing_submodule #$ imports=attr_clash.clashing_attr as=clashing_attr imports=attr_clash.non_clashing_submodule as=non_clashing_submodule

0 commit comments

Comments
 (0)