Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit d01d7ee

Browse files
committed
Python: Add documentation from DataFlowUtil::importNode
1 parent 4627799 commit d01d7ee

1 file changed

Lines changed: 42 additions & 1 deletion

File tree

python/ql/src/semmle/python/ApiGraphs.qll

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -257,7 +257,29 @@ module API {
257257
)
258258
}
259259

260-
/** Holds if `imp` is an import of a module named `name` */
260+
/**
261+
* Holds if `import_node` is an import of a module named `name`
262+
*
263+
* Ignores relative imports (`from ..foo import bar`).
264+
*
265+
* Note that for the statement `import pkg.mod`, the new variable introduced is `pkg` that is a
266+
* reference to the module `pkg`.
267+
*
268+
* This predicate handles (with optional `... as <new-name>`):
269+
* 1. `import <name>`
270+
* 2. `from <package> import <module>` when `<name> = <package> + "." + <module>`
271+
* 3. `from <module> import <member>` when `<name> = <module> + "." + <member>`
272+
*
273+
* Finally, in `from <module> import <member>` we consider the `ImportExpr` corresponding to
274+
* `<module>` to be a reference to that module.
275+
*
276+
* Note:
277+
* While it is technically possible that `import mypkg.foo` and `from mypkg import foo` can give different values,
278+
* it's highly unlikely that this will be a problem in production level code.
279+
* Example: If `mypkg/__init__.py` contains `foo = 42`, then `from mypkg import foo` will not import the module
280+
* `mypkg/foo.py` but the variable `foo` containing `42` -- however, `import mypkg.foo` will always cause `mypkg.foo`
281+
* to refer to the module.
282+
*/
261283
private predicate imports(DataFlow::Node import_node, string name) {
262284
exists(Variable var, Import imp, Alias alias |
263285
alias = imp.getAName() and
@@ -271,6 +293,25 @@ module API {
271293
import_node.asExpr() = alias.getValue()
272294
)
273295
or
296+
// Although it may seem superfluous to consider the `foo` part of `from foo import bar as baz` to
297+
// be a reference to a module (since that reference only makes sense locally within the `import`
298+
// statement), it's important for our use of type trackers to consider this local reference to
299+
// also refer to the `foo` module. That way, if one wants to track references to the `bar`
300+
// attribute using a type tracker, one can simply write
301+
//
302+
// ```ql
303+
// DataFlow::Node bar_attr_tracker(TypeTracker t) {
304+
// t.startInAttr("bar") and
305+
// result = foo_module_tracker()
306+
// or
307+
// exists(TypeTracker t2 | result = bar_attr_tracker(t2).track(t2, t))
308+
// }
309+
// ```
310+
//
311+
// Where `foo_module_tracker` is a type tracker that tracks references to the `foo` module.
312+
// Because named imports are modelled as `AttrRead`s, the statement `from foo import bar as baz`
313+
// is interpreted as if it was an assignment `baz = foo.bar`, which means `baz` gets tracked as a
314+
// reference to `foo.bar`, as desired.
274315
exists(ImportExpr imp_expr |
275316
not imp_expr.isRelative() and
276317
imp_expr.getName() = name and

0 commit comments

Comments
 (0)